/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! # Enum definitions for a `ComponentInterface`. //! //! This module converts enum definition from UDL into structures that can be //! added to a `ComponentInterface`. A declaration in the UDL like this: //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example {}; //! enum Example { //! "one", //! "two" //! }; //! # "##, "crate_name")?; //! # Ok::<(), anyhow::Error>(()) //! ``` //! //! Will result in a [`Enum`] member being added to the resulting [`crate::ComponentInterface`]: //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example {}; //! # enum Example { //! # "one", //! # "two" //! # }; //! # "##, "crate_name")?; //! let e = ci.get_enum_definition("Example").unwrap(); //! assert_eq!(e.name(), "Example"); //! assert_eq!(e.variants().len(), 2); //! assert_eq!(e.variants()[0].name(), "one"); //! assert_eq!(e.variants()[1].name(), "two"); //! # Ok::<(), anyhow::Error>(()) //! ``` //! //! Like in Rust, UniFFI enums can contain associated data, but this needs to be //! declared with a different syntax in order to work within the restrictions of //! WebIDL. A declaration like this: //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example {}; //! [Enum] //! interface Example { //! Zero(); //! One(u32 first); //! Two(u32 first, string second); //! }; //! # "##, "crate_name")?; //! # Ok::<(), anyhow::Error>(()) //! ``` //! //! Will result in an [`Enum`] member whose variants have associated fields: //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example {}; //! # [Enum] //! # interface ExampleWithData { //! # Zero(); //! # One(u32 first); //! # Two(u32 first, string second); //! # }; //! # "##, "crate_name")?; //! let e = ci.get_enum_definition("ExampleWithData").unwrap(); //! assert_eq!(e.name(), "ExampleWithData"); //! assert_eq!(e.variants().len(), 3); //! assert_eq!(e.variants()[0].name(), "Zero"); //! assert_eq!(e.variants()[0].fields().len(), 0); //! assert_eq!(e.variants()[1].name(), "One"); //! assert_eq!(e.variants()[1].fields().len(), 1); //! assert_eq!(e.variants()[1].fields()[0].name(), "first"); //! # Ok::<(), anyhow::Error>(()) //! ``` //! //! # Enums are also used to represent error definitions for a `ComponentInterface`. //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example {}; //! [Error] //! enum Example { //! "one", //! "two" //! }; //! # "##, "crate_name")?; //! # Ok::<(), anyhow::Error>(()) //! ``` //! //! Will result in an [`Enum`] member with fieldless variants being added to the resulting [`crate::ComponentInterface`]: //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example { //! # [Throws=Example] void func(); //! # }; //! # [Error] //! # enum Example { //! # "one", //! # "two" //! # }; //! # "##, "crate_name")?; //! let err = ci.get_enum_definition("Example").unwrap(); //! assert_eq!(err.name(), "Example"); //! assert_eq!(err.variants().len(), 2); //! assert_eq!(err.variants()[0].name(), "one"); //! assert_eq!(err.variants()[1].name(), "two"); //! assert_eq!(err.is_flat(), true); //! assert!(ci.is_name_used_as_error(&err.name())); //! # Ok::<(), anyhow::Error>(()) //! ``` //! //! A declaration in the UDL like this: //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example {}; //! [Error] //! interface Example { //! one(i16 code); //! two(string reason); //! three(i32 x, i32 y); //! }; //! # "##, "crate_name")?; //! # Ok::<(), anyhow::Error>(()) //! ``` //! //! Will result in an [`Enum`] member with variants that have fields being added to the resulting [`crate::ComponentInterface`]: //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example { //! # [Throws=Example] void func(); //! # }; //! # [Error] //! # interface Example { //! # one(); //! # two(string reason); //! # three(i32 x, i32 y); //! # }; //! # "##, "crate_name")?; //! let err = ci.get_enum_definition("Example").unwrap(); //! assert_eq!(err.name(), "Example"); //! assert_eq!(err.variants().len(), 3); //! assert_eq!(err.variants()[0].name(), "one"); //! assert_eq!(err.variants()[1].name(), "two"); //! assert_eq!(err.variants()[2].name(), "three"); //! assert_eq!(err.variants()[0].fields().len(), 0); //! assert_eq!(err.variants()[1].fields().len(), 1); //! assert_eq!(err.variants()[1].fields()[0].name(), "reason"); //! assert_eq!(err.variants()[2].fields().len(), 2); //! assert_eq!(err.variants()[2].fields()[0].name(), "x"); //! assert_eq!(err.variants()[2].fields()[1].name(), "y"); //! assert_eq!(err.is_flat(), false); //! assert!(ci.is_name_used_as_error(err.name())); //! # Ok::<(), anyhow::Error>(()) //! ```
use anyhow::Result; use uniffi_meta::{Checksum, EnumShape};
/// Represents an enum with named variants, each of which may have named /// and typed fields. /// /// Enums are passed across the FFI by serializing to a bytebuffer, with a /// i32 indicating the variant followed by the serialization of each field. #[derive(Debug, Clone, PartialEq, Eq, Checksum)] pubstructEnum { pub(super) name: String, pub(super) module_path: String, pub(super) discr_type: Option<Type>, pub(super) variants: Vec<Variant>, pub(super) shape: EnumShape, pub(super) non_exhaustive: bool, #[checksum_ignore] pub(super) docstring: Option<String>,
}
// Get the literal value to use for the specified variant's discriminant. // Follows Rust's rules when mixing specified and unspecified values; please // file a bug if you find a case where it does not. // However, it *does not* attempt to handle error cases - either cases where // a discriminant is not unique, or where a discriminant would overflow the // repr. The intention is that the Rust compiler itself will fail to build // in those cases, so by the time this get's run we can be confident these // error cases can't exist. pubfn variant_discr(&self, variant_index: usize) -> Result<Literal> { if variant_index >= self.variants.len() {
anyhow::bail!("Invalid variant index {variant_index}");
} letmut next = 0; letmut this; letmut this_lit = Literal::new_uint(0); for v inself.variants().iter().take(variant_index + 1) {
(this, this_lit) = match v.discr {
None => (
next, if (next as i64) < 0 {
Literal::new_int(next as i64)
} else {
Literal::new_uint(next)
},
),
Some(Literal::UInt(v, _, _)) => (v, Literal::new_uint(v)), // in-practice, Literal::Int == a negative number.
Some(Literal::Int(v, _, _)) => (v as u64, Literal::new_int(v)),
_ => anyhow::bail!("Invalid literal type {v:?}"),
};
next = this.wrapping_add(1);
}
Ok(this_lit)
}
/// Represents an individual variant in an Enum. /// /// Each variant has a name and zero or more fields. #[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)] pubstruct Variant { pub(super) name: String, pub(super) discr: Option<Literal>, pub(super) fields: Vec<Field>, #[checksum_ignore] pub(super) docstring: Option<String>,
}
[Enum]
interface TestEnumWithoutData {
One();
Two();
}; "#; let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
assert_eq!(ci.enum_definitions().count(), 3);
assert_eq!(ci.function_definitions().len(), 4);
// The "flat" enum with no associated data. let e = ci.get_enum_definition("TestEnum").unwrap();
assert!(e.is_flat());
assert_eq!(e.variants().len(), 2);
assert_eq!(
e.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
vec!["one", "two"]
);
assert_eq!(e.variants()[0].fields().len(), 0);
assert_eq!(e.variants()[1].fields().len(), 0);
// The enum declared via interface, but with no associated data. let ewd = ci.get_enum_definition("TestEnumWithoutData").unwrap();
assert_eq!(ewd.variants().len(), 2);
assert_eq!(
ewd.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
vec!["One", "Two"]
);
assert_eq!(ewd.variants()[0].fields().len(), 0);
assert_eq!(ewd.variants()[1].fields().len(), 0);
assert!(ewd.is_flat());
assert_eq!(ewd.shape, EnumShape::Enum);
// Flat enums pass over the FFI as bytebuffers. // (It might be nice to optimize these to pass as plain integers, but that's // difficult atop the current factoring of `ComponentInterface` and friends). let farg = ci.get_function_definition("takes_an_enum").unwrap();
assert_eq!(
farg.arguments()[0].as_type(), Type::Enum {
name: "TestEnum".into(),
module_path: "crate_name".into()
}
);
assert_eq!(
farg.ffi_func().arguments()[0].type_(),
FfiType::RustBuffer(None)
); let fret = ci.get_function_definition("returns_an_enum").unwrap();
assert!(
matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnum" && !ci.is_name_used_as_error(name))
);
assert!(matches!(
fret.ffi_func().return_type(),
Some(FfiType::RustBuffer(None))
));
// Enums with associated data pass over the FFI as bytebuffers. let farg = ci
.get_function_definition("takes_an_enum_with_data")
.unwrap();
assert_eq!(
farg.arguments()[0].as_type(), Type::Enum {
name: "TestEnumWithData".into(),
module_path: "crate_name".into()
}
);
assert_eq!(
farg.ffi_func().arguments()[0].type_(),
FfiType::RustBuffer(None)
); let fret = ci
.get_function_definition("returns_an_enum_with_data")
.unwrap();
assert!(
matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnumWithData" && !ci.is_name_used_as_error(name))
);
assert!(matches!(
fret.ffi_func().return_type(),
Some(FfiType::RustBuffer(None))
));
}
// Tests for [Error], which are represented as `Enum` #[test] fn test_variants() { const UDL: &str = r#"
namespace test{
[Throws=Testing]
void func();
};
[Error] enum Testing { "one", "two", "three" }; "#; let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
assert_eq!(ci.enum_definitions().count(), 1); let error = ci.get_enum_definition("Testing").unwrap();
assert_eq!(
error
.variants()
.iter()
.map(|v| v.name())
.collect::<Vec<&str>>(),
vec!("one", "two", "three")
);
assert!(error.is_flat());
assert!(ci.is_name_used_as_error(&error.name));
}
#[test] fn test_duplicate_error_variants() { const UDL: &str = r#"
namespace test{}; // Weird, but currently allowed! // We should probably disallow this...
[Error] enum Testing { "one", "two", "one" }; "#; let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
assert_eq!(ci.enum_definitions().count(), 1);
assert_eq!(
ci.get_enum_definition("Testing").unwrap().variants().len(), 3
);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.