Backed out 5 changesets (bug 1890092, bug 1888683) for causing build bustages & crash...
[gecko.git] / third_party / rust / uniffi_bindgen / src / interface / enum_.rs
blob82baf1dd50f8e4a7f1337a4b32865072061312f5
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`.
6 //!
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:
9 //!
10 //! ```
11 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
12 //! # namespace example {};
13 //! enum Example {
14 //!   "one",
15 //!   "two"
16 //! };
17 //! # "##, "crate_name")?;
18 //! # Ok::<(), anyhow::Error>(())
19 //! ```
20 //!
21 //! Will result in a [`Enum`] member being added to the resulting [`crate::ComponentInterface`]:
22 //!
23 //! ```
24 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
25 //! # namespace example {};
26 //! # enum Example {
27 //! #   "one",
28 //! #   "two"
29 //! # };
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>(())
37 //! ```
38 //!
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:
42 //!
43 //! ```
44 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
45 //! # namespace example {};
46 //! [Enum]
47 //! interface Example {
48 //!   Zero();
49 //!   One(u32 first);
50 //!   Two(u32 first, string second);
51 //! };
52 //! # "##, "crate_name")?;
53 //! # Ok::<(), anyhow::Error>(())
54 //! ```
55 //!
56 //! Will result in an [`Enum`] member whose variants have associated fields:
57 //!
58 //! ```
59 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
60 //! # namespace example {};
61 //! # [Enum]
62 //! # interface ExampleWithData {
63 //! #   Zero();
64 //! #   One(u32 first);
65 //! #   Two(u32 first, string second);
66 //! # };
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>(())
77 //! ```
78 //!
79 //! # Enums are also used to represent error definitions for a `ComponentInterface`.
80 //!
81 //! ```
82 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
83 //! # namespace example {};
84 //! [Error]
85 //! enum Example {
86 //!   "one",
87 //!   "two"
88 //! };
89 //! # "##, "crate_name")?;
90 //! # Ok::<(), anyhow::Error>(())
91 //! ```
92 //!
93 //! Will result in an [`Enum`] member with fieldless variants being added to the resulting [`crate::ComponentInterface`]:
94 //!
95 //! ```
96 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
97 //! # namespace example {};
98 //! # [Error]
99 //! # enum Example {
100 //! #   "one",
101 //! #   "two"
102 //! # };
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>(())
112 //! ```
114 //! A declaration in the UDL like this:
116 //! ```
117 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
118 //! # namespace example {};
119 //! [Error]
120 //! interface Example {
121 //!   one(i16 code);
122 //!   two(string reason);
123 //!   three(i32 x, i32 y);
124 //! };
125 //! # "##, "crate_name")?;
126 //! # Ok::<(), anyhow::Error>(())
127 //! ```
129 //! Will result in an [`Enum`] member with variants that have fields being added to the resulting [`crate::ComponentInterface`]:
131 //! ```
132 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
133 //! # namespace example {};
134 //! # [Error]
135 //! # interface Example {
136 //! #   one();
137 //! #   two(string reason);
138 //! #   three(i32 x, i32 y);
139 //! # };
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>(())
156 //! ```
158 use anyhow::Result;
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)]
170 pub struct Enum {
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
180     //   similar.
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.
184     //
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,
194 impl Enum {
195     pub fn name(&self) -> &str {
196         &self.name
197     }
199     pub fn variants(&self) -> &[Variant] {
200         &self.variants
201     }
203     pub fn is_flat(&self) -> bool {
204         self.flat
205     }
207     pub fn iter_types(&self) -> TypeIterator<'_> {
208         Box::new(self.variants.iter().flat_map(Variant::iter_types))
209     }
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
215         // is actually flat.
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.
218         Ok(Self {
219             name: meta.name,
220             module_path: meta.module_path,
221             variants: meta
222                 .variants
223                 .into_iter()
224                 .map(TryInto::try_into)
225                 .collect::<Result<_>>()?,
226             flat,
227         })
228     }
231 impl AsType for Enum {
232     fn as_type(&self) -> Type {
233         Type::Enum {
234             name: self.name.clone(),
235             module_path: self.module_path.clone(),
236         }
237     }
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)]
244 pub struct Variant {
245     pub(super) name: String,
246     pub(super) fields: Vec<Field>,
249 impl Variant {
250     pub fn name(&self) -> &str {
251         &self.name
252     }
254     pub fn fields(&self) -> &[Field] {
255         &self.fields
256     }
258     pub fn has_fields(&self) -> bool {
259         !self.fields.is_empty()
260     }
262     pub fn iter_types(&self) -> TypeIterator<'_> {
263         Box::new(self.fields.iter().flat_map(Field::iter_types))
264     }
267 impl TryFrom<uniffi_meta::VariantMetadata> for Variant {
268     type Error = anyhow::Error;
270     fn try_from(meta: uniffi_meta::VariantMetadata) -> Result<Self> {
271         Ok(Self {
272             name: meta.name,
273             fields: meta
274                 .fields
275                 .into_iter()
276                 .map(TryInto::try_into)
277                 .collect::<Result<_>>()?,
278         })
279     }
282 #[cfg(test)]
283 mod test {
284     use super::super::{ComponentInterface, FfiType};
285     use super::*;
287     #[test]
288     fn test_duplicate_variants() {
289         const UDL: &str = r#"
290             namespace test{};
291             // Weird, but currently allowed!
292             // We should probably disallow this...
293             enum Testing { "one", "two", "one" };
294         "#;
295         let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
296         assert_eq!(ci.enum_definitions().count(), 1);
297         assert_eq!(
298             ci.get_enum_definition("Testing").unwrap().variants().len(),
299             3
300         );
301     }
303     #[test]
304     fn test_associated_data() {
305         const UDL: &str = r#"
306             namespace test {
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();
311             };
313             enum TestEnum { "one", "two" };
315             [Enum]
316             interface TestEnumWithData {
317                 Zero();
318                 One(u32 first);
319                 Two(u32 first, string second);
320             };
322             [Enum]
323             interface TestEnumWithoutData {
324                 One();
325                 Two();
326             };
327         "#;
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);
336         assert_eq!(
337             e.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
338             vec!["one", "two"]
339         );
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);
347         assert_eq!(
348             ed.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
349             vec!["Zero", "One", "Two"]
350         );
351         assert_eq!(ed.variants()[0].fields().len(), 0);
352         assert_eq!(
353             ed.variants()[1]
354                 .fields()
355                 .iter()
356                 .map(|f| f.name())
357                 .collect::<Vec<_>>(),
358             vec!["first"]
359         );
360         assert_eq!(
361             ed.variants()[1]
362                 .fields()
363                 .iter()
364                 .map(|f| f.as_type())
365                 .collect::<Vec<_>>(),
366             vec![Type::UInt32]
367         );
368         assert_eq!(
369             ed.variants()[2]
370                 .fields()
371                 .iter()
372                 .map(|f| f.name())
373                 .collect::<Vec<_>>(),
374             vec!["first", "second"]
375         );
376         assert_eq!(
377             ed.variants()[2]
378                 .fields()
379                 .iter()
380                 .map(|f| f.as_type())
381                 .collect::<Vec<_>>(),
382             vec![Type::UInt32, Type::String]
383         );
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);
388         assert_eq!(
389             ewd.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
390             vec!["One", "Two"]
391         );
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();
399         assert_eq!(
400             farg.arguments()[0].as_type(),
401             Type::Enum {
402                 name: "TestEnum".into(),
403                 module_path: "crate_name".into()
404             }
405         );
406         assert_eq!(
407             farg.ffi_func().arguments()[0].type_(),
408             FfiType::RustBuffer(None)
409         );
410         let fret = ci.get_function_definition("returns_an_enum").unwrap();
411         assert!(
412             matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnum" && !ci.is_name_used_as_error(name))
413         );
414         assert!(matches!(
415             fret.ffi_func().return_type(),
416             Some(FfiType::RustBuffer(None))
417         ));
419         // Enums with associated data pass over the FFI as bytebuffers.
420         let farg = ci
421             .get_function_definition("takes_an_enum_with_data")
422             .unwrap();
423         assert_eq!(
424             farg.arguments()[0].as_type(),
425             Type::Enum {
426                 name: "TestEnumWithData".into(),
427                 module_path: "crate_name".into()
428             }
429         );
430         assert_eq!(
431             farg.ffi_func().arguments()[0].type_(),
432             FfiType::RustBuffer(None)
433         );
434         let fret = ci
435             .get_function_definition("returns_an_enum_with_data")
436             .unwrap();
437         assert!(
438             matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnumWithData" && !ci.is_name_used_as_error(name))
439         );
440         assert!(matches!(
441             fret.ffi_func().return_type(),
442             Some(FfiType::RustBuffer(None))
443         ));
444     }
446     // Tests for [Error], which are represented as `Enum`
447     #[test]
448     fn test_variants() {
449         const UDL: &str = r#"
450             namespace test{};
451             [Error]
452             enum Testing { "one", "two", "three" };
453         "#;
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();
457         assert_eq!(
458             error
459                 .variants()
460                 .iter()
461                 .map(|v| v.name())
462                 .collect::<Vec<&str>>(),
463             vec!("one", "two", "three")
464         );
465         assert!(error.is_flat());
466         assert!(ci.is_name_used_as_error(&error.name));
467     }
469     #[test]
470     fn test_duplicate_error_variants() {
471         const UDL: &str = r#"
472             namespace test{};
473             // Weird, but currently allowed!
474             // We should probably disallow this...
475             [Error]
476             enum Testing { "one", "two", "one" };
477         "#;
478         let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
479         assert_eq!(ci.enum_definitions().count(), 1);
480         assert_eq!(
481             ci.get_enum_definition("Testing").unwrap().variants().len(),
482             3
483         );
484     }
486     #[test]
487     fn test_variant_data() {
488         const UDL: &str = r#"
489             namespace test{};
491             [Error]
492             interface Testing {
493                 One(string reason);
494                 Two(u8 code);
495             };
496         "#;
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();
500         assert_eq!(
501             error
502                 .variants()
503                 .iter()
504                 .map(|v| v.name())
505                 .collect::<Vec<&str>>(),
506             vec!("One", "Two")
507         );
508         assert!(!error.is_flat());
509         assert!(ci.is_name_used_as_error(&error.name));
510     }
512     #[test]
513     fn test_enum_variant_named_error() {
514         const UDL: &str = r#"
515             namespace test{};
517             [Enum]
518             interface Testing {
519                 Normal(string first);
520                 Error(string first);
521             };
522         "#;
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();
526         assert_eq!(
527             testing.variants()[0]
528                 .fields()
529                 .iter()
530                 .map(|f| f.name())
531                 .collect::<Vec<_>>(),
532             vec!["first"]
533         );
534         assert_eq!(
535             testing.variants()[0]
536                 .fields()
537                 .iter()
538                 .map(|f| f.as_type())
539                 .collect::<Vec<_>>(),
540             vec![Type::String]
541         );
542         assert_eq!(
543             testing.variants()[1]
544                 .fields()
545                 .iter()
546                 .map(|f| f.name())
547                 .collect::<Vec<_>>(),
548             vec!["first"]
549         );
550         assert_eq!(
551             testing.variants()[1]
552                 .fields()
553                 .iter()
554                 .map(|f| f.as_type())
555                 .collect::<Vec<_>>(),
556             vec![Type::String]
557         );
558         assert_eq!(
559             testing
560                 .variants()
561                 .iter()
562                 .map(|v| v.name())
563                 .collect::<Vec<_>>(),
564             vec!["Normal", "Error"]
565         );
566     }