1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 //! # Enum definitions for a `ComponentInterface`.
7 //! This module converts enum definition from UDL into structures that can be
8 //! added to a `ComponentInterface`. A declaration in the UDL like this:
11 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
12 //! # namespace example {};
17 //! # "##, "crate_name")?;
18 //! # Ok::<(), anyhow::Error>(())
21 //! Will result in a [`Enum`] member being added to the resulting [`crate::ComponentInterface`]:
24 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
25 //! # namespace example {};
30 //! # "##, "crate_name")?;
31 //! let e = ci.get_enum_definition("Example").unwrap();
32 //! assert_eq!(e.name(), "Example");
33 //! assert_eq!(e.variants().len(), 2);
34 //! assert_eq!(e.variants()[0].name(), "one");
35 //! assert_eq!(e.variants()[1].name(), "two");
36 //! # Ok::<(), anyhow::Error>(())
39 //! Like in Rust, UniFFI enums can contain associated data, but this needs to be
40 //! declared with a different syntax in order to work within the restrictions of
41 //! WebIDL. A declaration like this:
44 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
45 //! # namespace example {};
47 //! interface Example {
50 //! Two(u32 first, string second);
52 //! # "##, "crate_name")?;
53 //! # Ok::<(), anyhow::Error>(())
56 //! Will result in an [`Enum`] member whose variants have associated fields:
59 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
60 //! # namespace example {};
62 //! # interface ExampleWithData {
65 //! # Two(u32 first, string second);
67 //! # "##, "crate_name")?;
68 //! let e = ci.get_enum_definition("ExampleWithData").unwrap();
69 //! assert_eq!(e.name(), "ExampleWithData");
70 //! assert_eq!(e.variants().len(), 3);
71 //! assert_eq!(e.variants()[0].name(), "Zero");
72 //! assert_eq!(e.variants()[0].fields().len(), 0);
73 //! assert_eq!(e.variants()[1].name(), "One");
74 //! assert_eq!(e.variants()[1].fields().len(), 1);
75 //! assert_eq!(e.variants()[1].fields()[0].name(), "first");
76 //! # Ok::<(), anyhow::Error>(())
79 //! # Enums are also used to represent error definitions for a `ComponentInterface`.
82 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
83 //! # namespace example {};
89 //! # "##, "crate_name")?;
90 //! # Ok::<(), anyhow::Error>(())
93 //! Will result in an [`Enum`] member with fieldless variants being added to the resulting [`crate::ComponentInterface`]:
96 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
97 //! # namespace example {};
103 //! # "##, "crate_name")?;
104 //! let err = ci.get_enum_definition("Example").unwrap();
105 //! assert_eq!(err.name(), "Example");
106 //! assert_eq!(err.variants().len(), 2);
107 //! assert_eq!(err.variants()[0].name(), "one");
108 //! assert_eq!(err.variants()[1].name(), "two");
109 //! assert_eq!(err.is_flat(), true);
110 //! assert!(ci.is_name_used_as_error(&err.name()));
111 //! # Ok::<(), anyhow::Error>(())
114 //! A declaration in the UDL like this:
117 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
118 //! # namespace example {};
120 //! interface Example {
122 //! two(string reason);
123 //! three(i32 x, i32 y);
125 //! # "##, "crate_name")?;
126 //! # Ok::<(), anyhow::Error>(())
129 //! Will result in an [`Enum`] member with variants that have fields being added to the resulting [`crate::ComponentInterface`]:
132 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
133 //! # namespace example {};
135 //! # interface Example {
137 //! # two(string reason);
138 //! # three(i32 x, i32 y);
140 //! # "##, "crate_name")?;
141 //! let err = ci.get_enum_definition("Example").unwrap();
142 //! assert_eq!(err.name(), "Example");
143 //! assert_eq!(err.variants().len(), 3);
144 //! assert_eq!(err.variants()[0].name(), "one");
145 //! assert_eq!(err.variants()[1].name(), "two");
146 //! assert_eq!(err.variants()[2].name(), "three");
147 //! assert_eq!(err.variants()[0].fields().len(), 0);
148 //! assert_eq!(err.variants()[1].fields().len(), 1);
149 //! assert_eq!(err.variants()[1].fields()[0].name(), "reason");
150 //! assert_eq!(err.variants()[2].fields().len(), 2);
151 //! assert_eq!(err.variants()[2].fields()[0].name(), "x");
152 //! assert_eq!(err.variants()[2].fields()[1].name(), "y");
153 //! assert_eq!(err.is_flat(), false);
154 //! assert!(ci.is_name_used_as_error(err.name()));
155 //! # Ok::<(), anyhow::Error>(())
159 use uniffi_meta::Checksum;
161 use super::record::Field;
162 use super::{AsType, Type, TypeIterator};
164 /// Represents an enum with named variants, each of which may have named
165 /// and typed fields.
167 /// Enums are passed across the FFI by serializing to a bytebuffer, with a
168 /// i32 indicating the variant followed by the serialization of each field.
169 #[derive(Debug, Clone, PartialEq, Eq, Checksum)]
171 pub(super) name: String,
172 pub(super) module_path: String,
173 pub(super) variants: Vec<Variant>,
174 // NOTE: `flat` is a misleading name and to make matters worse, has 2 different
175 // meanings depending on the context :(
176 // * When used as part of Rust scaffolding generation, it means "is this enum
177 // used with an Error, and that error should we lowered to foreign bindings
178 // by converting each variant to a string and lowering the variant with that
179 // string?". In that context, it should probably be called `lowered_as_string` or
181 // * When used as part of bindings generation, it means "does this enum have only
182 // variants with no associated data"? The foreign binding generators are likely
183 // to generate significantly different versions of the enum based on that value.
185 // The reason it is described as "has 2 different meanings" by way of example:
186 // * For an Enum described as being a flat error, but the enum itself has variants with data,
187 // `flat` will be `true` for the Enum when generating scaffolding and `false` when
188 // generating bindings.
189 // * For an Enum not used as an error but which has no variants with data, `flat` will be
190 // false when generating the scaffolding but `true` when generating bindings.
191 pub(super) flat: bool,
195 pub fn name(&self) -> &str {
199 pub fn variants(&self) -> &[Variant] {
203 pub fn is_flat(&self) -> bool {
207 pub fn iter_types(&self) -> TypeIterator<'_> {
208 Box::new(self.variants.iter().flat_map(Variant::iter_types))
211 // Sadly can't use TryFrom due to the 'is_flat' complication.
212 pub fn try_from_meta(meta: uniffi_meta::EnumMetadata, flat: bool) -> Result<Self> {
213 // This is messy - error enums are considered "flat" if the user
214 // opted in via a special attribute, regardless of whether the enum
216 // Real enums are considered flat iff they are actually flat.
217 // We don't have that context here, so this is handled by our caller.
220 module_path: meta.module_path,
224 .map(TryInto::try_into)
225 .collect::<Result<_>>()?,
231 impl AsType for Enum {
232 fn as_type(&self) -> Type {
234 name: self.name.clone(),
235 module_path: self.module_path.clone(),
240 /// Represents an individual variant in an Enum.
242 /// Each variant has a name and zero or more fields.
243 #[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)]
245 pub(super) name: String,
246 pub(super) fields: Vec<Field>,
250 pub fn name(&self) -> &str {
254 pub fn fields(&self) -> &[Field] {
258 pub fn has_fields(&self) -> bool {
259 !self.fields.is_empty()
262 pub fn iter_types(&self) -> TypeIterator<'_> {
263 Box::new(self.fields.iter().flat_map(Field::iter_types))
267 impl TryFrom<uniffi_meta::VariantMetadata> for Variant {
268 type Error = anyhow::Error;
270 fn try_from(meta: uniffi_meta::VariantMetadata) -> Result<Self> {
276 .map(TryInto::try_into)
277 .collect::<Result<_>>()?,
284 use super::super::{ComponentInterface, FfiType};
288 fn test_duplicate_variants() {
289 const UDL: &str = r#"
291 // Weird, but currently allowed!
292 // We should probably disallow this...
293 enum Testing { "one", "two", "one" };
295 let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
296 assert_eq!(ci.enum_definitions().count(), 1);
298 ci.get_enum_definition("Testing").unwrap().variants().len(),
304 fn test_associated_data() {
305 const UDL: &str = r#"
307 void takes_an_enum(TestEnum e);
308 void takes_an_enum_with_data(TestEnumWithData ed);
309 TestEnum returns_an_enum();
310 TestEnumWithData returns_an_enum_with_data();
313 enum TestEnum { "one", "two" };
316 interface TestEnumWithData {
319 Two(u32 first, string second);
323 interface TestEnumWithoutData {
328 let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
329 assert_eq!(ci.enum_definitions().count(), 3);
330 assert_eq!(ci.function_definitions().len(), 4);
332 // The "flat" enum with no associated data.
333 let e = ci.get_enum_definition("TestEnum").unwrap();
334 assert!(e.is_flat());
335 assert_eq!(e.variants().len(), 2);
337 e.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
340 assert_eq!(e.variants()[0].fields().len(), 0);
341 assert_eq!(e.variants()[1].fields().len(), 0);
343 // The enum with associated data.
344 let ed = ci.get_enum_definition("TestEnumWithData").unwrap();
345 assert!(!ed.is_flat());
346 assert_eq!(ed.variants().len(), 3);
348 ed.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
349 vec!["Zero", "One", "Two"]
351 assert_eq!(ed.variants()[0].fields().len(), 0);
357 .collect::<Vec<_>>(),
364 .map(|f| f.as_type())
365 .collect::<Vec<_>>(),
373 .collect::<Vec<_>>(),
374 vec!["first", "second"]
380 .map(|f| f.as_type())
381 .collect::<Vec<_>>(),
382 vec![Type::UInt32, Type::String]
385 // The enum declared via interface, but with no associated data.
386 let ewd = ci.get_enum_definition("TestEnumWithoutData").unwrap();
387 assert_eq!(ewd.variants().len(), 2);
389 ewd.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
392 assert_eq!(ewd.variants()[0].fields().len(), 0);
393 assert_eq!(ewd.variants()[1].fields().len(), 0);
395 // Flat enums pass over the FFI as bytebuffers.
396 // (It might be nice to optimize these to pass as plain integers, but that's
397 // difficult atop the current factoring of `ComponentInterface` and friends).
398 let farg = ci.get_function_definition("takes_an_enum").unwrap();
400 farg.arguments()[0].as_type(),
402 name: "TestEnum".into(),
403 module_path: "crate_name".into()
407 farg.ffi_func().arguments()[0].type_(),
408 FfiType::RustBuffer(None)
410 let fret = ci.get_function_definition("returns_an_enum").unwrap();
412 matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnum" && !ci.is_name_used_as_error(name))
415 fret.ffi_func().return_type(),
416 Some(FfiType::RustBuffer(None))
419 // Enums with associated data pass over the FFI as bytebuffers.
421 .get_function_definition("takes_an_enum_with_data")
424 farg.arguments()[0].as_type(),
426 name: "TestEnumWithData".into(),
427 module_path: "crate_name".into()
431 farg.ffi_func().arguments()[0].type_(),
432 FfiType::RustBuffer(None)
435 .get_function_definition("returns_an_enum_with_data")
438 matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnumWithData" && !ci.is_name_used_as_error(name))
441 fret.ffi_func().return_type(),
442 Some(FfiType::RustBuffer(None))
446 // Tests for [Error], which are represented as `Enum`
449 const UDL: &str = r#"
452 enum Testing { "one", "two", "three" };
454 let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
455 assert_eq!(ci.enum_definitions().count(), 1);
456 let error = ci.get_enum_definition("Testing").unwrap();
462 .collect::<Vec<&str>>(),
463 vec!("one", "two", "three")
465 assert!(error.is_flat());
466 assert!(ci.is_name_used_as_error(&error.name));
470 fn test_duplicate_error_variants() {
471 const UDL: &str = r#"
473 // Weird, but currently allowed!
474 // We should probably disallow this...
476 enum Testing { "one", "two", "one" };
478 let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
479 assert_eq!(ci.enum_definitions().count(), 1);
481 ci.get_enum_definition("Testing").unwrap().variants().len(),
487 fn test_variant_data() {
488 const UDL: &str = r#"
497 let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
498 assert_eq!(ci.enum_definitions().count(), 1);
499 let error: &Enum = ci.get_enum_definition("Testing").unwrap();
505 .collect::<Vec<&str>>(),
508 assert!(!error.is_flat());
509 assert!(ci.is_name_used_as_error(&error.name));
513 fn test_enum_variant_named_error() {
514 const UDL: &str = r#"
519 Normal(string first);
523 let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
524 assert_eq!(ci.enum_definitions().count(), 1);
525 let testing: &Enum = ci.get_enum_definition("Testing").unwrap();
527 testing.variants()[0]
531 .collect::<Vec<_>>(),
535 testing.variants()[0]
538 .map(|f| f.as_type())
539 .collect::<Vec<_>>(),
543 testing.variants()[1]
547 .collect::<Vec<_>>(),
551 testing.variants()[1]
554 .map(|f| f.as_type())
555 .collect::<Vec<_>>(),
563 .collect::<Vec<_>>(),
564 vec!["Normal", "Error"]