use anyhow::{bail, Result};
use uniffi_meta::Checksum;
use super::record::Field;
use super::types::{Type, TypeIterator};
use super::{APIConverter, ComponentInterface};
#[derive(Debug, Clone, PartialEq, Eq, Checksum)]
pub struct Enum {
pub(super) name: String,
pub(super) variants: Vec<Variant>,
pub(super) flat: bool,
}
impl Enum {
pub fn name(&self) -> &str {
&self.name
}
pub fn type_(&self) -> Type {
Type::Enum(self.name.clone())
}
pub fn variants(&self) -> &[Variant] {
&self.variants
}
pub fn is_flat(&self) -> bool {
self.flat
}
pub fn iter_types(&self) -> TypeIterator<'_> {
Box::new(self.variants.iter().flat_map(Variant::iter_types))
}
}
impl From<uniffi_meta::EnumMetadata> for Enum {
fn from(meta: uniffi_meta::EnumMetadata) -> Self {
let flat = meta.variants.iter().all(|v| v.fields.is_empty());
Self {
name: meta.name,
variants: meta.variants.into_iter().map(Into::into).collect(),
flat,
}
}
}
impl APIConverter<Enum> for weedle::EnumDefinition<'_> {
fn convert(&self, _ci: &mut ComponentInterface) -> Result<Enum> {
Ok(Enum {
name: self.identifier.0.to_string(),
variants: self
.values
.body
.list
.iter()
.map::<Result<_>, _>(|v| {
Ok(Variant {
name: v.0.to_string(),
..Default::default()
})
})
.collect::<Result<Vec<_>>>()?,
flat: true,
})
}
}
impl APIConverter<Enum> for weedle::InterfaceDefinition<'_> {
fn convert(&self, ci: &mut ComponentInterface) -> Result<Enum> {
if self.inheritance.is_some() {
bail!("interface inheritance is not supported for enum interfaces");
}
Ok(Enum {
name: self.identifier.0.to_string(),
variants: self
.members
.body
.iter()
.map::<Result<Variant>, _>(|member| match member {
weedle::interface::InterfaceMember::Operation(t) => Ok(t.convert(ci)?),
_ => bail!(
"interface member type {:?} not supported in enum interface",
member
),
})
.collect::<Result<Vec<_>>>()?,
flat: false,
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)]
pub struct Variant {
pub(super) name: String,
pub(super) fields: Vec<Field>,
}
impl Variant {
pub fn name(&self) -> &str {
&self.name
}
pub fn fields(&self) -> &[Field] {
&self.fields
}
pub fn has_fields(&self) -> bool {
!self.fields.is_empty()
}
pub fn iter_types(&self) -> TypeIterator<'_> {
Box::new(self.fields.iter().flat_map(Field::iter_types))
}
}
impl From<uniffi_meta::VariantMetadata> for Variant {
fn from(meta: uniffi_meta::VariantMetadata) -> Self {
Self {
name: meta.name,
fields: meta.fields.into_iter().map(Into::into).collect(),
}
}
}
impl APIConverter<Variant> for weedle::interface::OperationInterfaceMember<'_> {
fn convert(&self, ci: &mut ComponentInterface) -> Result<Variant> {
if self.special.is_some() {
bail!("special operations not supported");
}
if let Some(weedle::interface::StringifierOrStatic::Stringifier(_)) = self.modifier {
bail!("stringifiers are not supported");
}
if self.identifier.is_some() {
bail!("enum interface members must not have a method name");
}
let name: String = {
use weedle::types::{
NonAnyType::Identifier, ReturnType, SingleType::NonAny, Type::Single,
};
match &self.return_type {
ReturnType::Type(Single(NonAny(Identifier(id)))) => id.type_.0.to_owned(),
_ => bail!("enum interface members must have plain identifiers as names"),
}
};
Ok(Variant {
name,
fields: self
.args
.body
.list
.iter()
.map(|arg| arg.convert(ci))
.collect::<Result<Vec<_>>>()?,
})
}
}
impl APIConverter<Field> for weedle::argument::Argument<'_> {
fn convert(&self, ci: &mut ComponentInterface) -> Result<Field> {
match self {
weedle::argument::Argument::Single(t) => t.convert(ci),
weedle::argument::Argument::Variadic(_) => bail!("variadic arguments not supported"),
}
}
}
impl APIConverter<Field> for weedle::argument::SingleArgument<'_> {
fn convert(&self, ci: &mut ComponentInterface) -> Result<Field> {
let type_ = ci.resolve_type_expression(&self.type_)?;
if let Type::Object(_) = type_ {
bail!("Objects cannot currently be used in enum variant data");
}
if self.default.is_some() {
bail!("enum interface variant fields must not have default values");
}
if self.attributes.is_some() {
bail!("enum interface variant fields must not have attributes");
}
Ok(Field {
name: self.identifier.0.to_string(),
type_,
default: None,
})
}
}
#[cfg(test)]
mod test {
use super::super::ffi::FfiType;
use super::*;
#[test]
fn test_duplicate_variants() {
const UDL: &str = r#"
namespace test{};
// Weird, but currently allowed!
// We should probably disallow this...
enum Testing { "one", "two", "one" };
"#;
let ci = ComponentInterface::from_webidl(UDL).unwrap();
assert_eq!(ci.enum_definitions().count(), 1);
assert_eq!(
ci.get_enum_definition("Testing").unwrap().variants().len(),
3
);
}
#[test]
fn test_associated_data() {
const UDL: &str = r##"
namespace test {
void takes_an_enum(TestEnum e);
void takes_an_enum_with_data(TestEnumWithData ed);
TestEnum returns_an_enum();
TestEnumWithData returns_an_enum_with_data();
};
enum TestEnum { "one", "two" };
[Enum]
interface TestEnumWithData {
Zero();
One(u32 first);
Two(u32 first, string second);
};
[Enum]
interface TestEnumWithoutData {
One();
Two();
};
"##;
let ci = ComponentInterface::from_webidl(UDL).unwrap();
assert_eq!(ci.enum_definitions().count(), 3);
assert_eq!(ci.function_definitions().len(), 4);
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);
let ed = ci.get_enum_definition("TestEnumWithData").unwrap();
assert!(!ed.is_flat());
assert_eq!(ed.variants().len(), 3);
assert_eq!(
ed.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
vec!["Zero", "One", "Two"]
);
assert_eq!(ed.variants()[0].fields().len(), 0);
assert_eq!(
ed.variants()[1]
.fields()
.iter()
.map(|f| f.name())
.collect::<Vec<_>>(),
vec!["first"]
);
assert_eq!(
ed.variants()[1]
.fields()
.iter()
.map(|f| f.type_())
.collect::<Vec<_>>(),
vec![&Type::UInt32]
);
assert_eq!(
ed.variants()[2]
.fields()
.iter()
.map(|f| f.name())
.collect::<Vec<_>>(),
vec!["first", "second"]
);
assert_eq!(
ed.variants()[2]
.fields()
.iter()
.map(|f| f.type_())
.collect::<Vec<_>>(),
vec![&Type::UInt32, &Type::String]
);
let ewd = ci.get_enum_definition("TestEnumWithoutData").unwrap();
assert!(!ewd.is_flat());
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);
let farg = ci.get_function_definition("takes_an_enum").unwrap();
assert_eq!(*farg.arguments()[0].type_(), Type::Enum("TestEnum".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(nm)) if nm == "TestEnum"));
assert!(matches!(
fret.ffi_func().return_type(),
Some(FfiType::RustBuffer(None))
));
let farg = ci
.get_function_definition("takes_an_enum_with_data")
.unwrap();
assert_eq!(
*farg.arguments()[0].type_(),
Type::Enum("TestEnumWithData".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(nm)) if nm == "TestEnumWithData"));
assert!(matches!(
fret.ffi_func().return_type(),
Some(FfiType::RustBuffer(None))
));
}
}