diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/uniffi_udl/src/attributes.rs | 199 | ||||
-rw-r--r-- | third_party/rust/uniffi_udl/src/collectors.rs | 18 | ||||
-rw-r--r-- | third_party/rust/uniffi_udl/src/converters/callables.rs | 16 | ||||
-rw-r--r-- | third_party/rust/uniffi_udl/src/converters/enum_.rs | 79 | ||||
-rw-r--r-- | third_party/rust/uniffi_udl/src/converters/interface.rs | 6 | ||||
-rw-r--r-- | third_party/rust/uniffi_udl/src/converters/mod.rs | 10 | ||||
-rw-r--r-- | third_party/rust/uniffi_udl/src/finder.rs | 60 | ||||
-rw-r--r-- | third_party/rust/uniffi_udl/src/lib.rs | 2 | ||||
-rw-r--r-- | third_party/rust/uniffi_udl/src/literal.rs | 8 | ||||
-rw-r--r-- | third_party/rust/uniffi_udl/src/resolver.rs | 1 |
10 files changed, 282 insertions, 117 deletions
diff --git a/third_party/rust/uniffi_udl/src/attributes.rs b/third_party/rust/uniffi_udl/src/attributes.rs index f06b4f29c1..7bb05808c4 100644 --- a/third_party/rust/uniffi_udl/src/attributes.rs +++ b/third_party/rust/uniffi_udl/src/attributes.rs @@ -37,10 +37,29 @@ pub(super) enum Attribute { kind: ExternalKind, export: bool, }, + Rust { + kind: RustKind, + }, // Custom type on the scaffolding side Custom, // The interface described is implemented as a trait. Trait, + // Modifies `Trait` to enable foreign implementations (callback interfaces) + WithForeign, + Async, + NonExhaustive, +} + +// A type defined in Rust via procmacros but which should be available +// in UDL. +#[derive(Debug, Copy, Clone, Checksum)] +pub(super) enum RustKind { + Object, + CallbackTrait, + Trait, + Record, + Enum, + CallbackInterface, } impl Attribute { @@ -67,6 +86,9 @@ impl TryFrom<&weedle::attribute::ExtendedAttribute<'_>> for Attribute { "Error" => Ok(Attribute::Error), "Custom" => Ok(Attribute::Custom), "Trait" => Ok(Attribute::Trait), + "WithForeign" => Ok(Attribute::WithForeign), + "Async" => Ok(Attribute::Async), + "NonExhaustive" => Ok(Attribute::NonExhaustive), _ => anyhow::bail!("ExtendedAttributeNoArgs not supported: {:?}", (attr.0).0), }, // Matches assignment-style attributes like ["Throws=Error"] @@ -95,6 +117,19 @@ impl TryFrom<&weedle::attribute::ExtendedAttribute<'_>> for Attribute { kind: ExternalKind::Interface, export: true, }), + "ExternalTrait" => Ok(Attribute::External { + crate_name: name_from_id_or_string(&identity.rhs), + kind: ExternalKind::Trait, + export: false, + }), + "ExternalTraitExport" => Ok(Attribute::External { + crate_name: name_from_id_or_string(&identity.rhs), + kind: ExternalKind::Trait, + export: true, + }), + "Rust" => Ok(Attribute::Rust { + kind: rust_kind_from_id_or_string(&identity.rhs)?, + }), _ => anyhow::bail!( "Attribute identity Identifier not supported: {:?}", identity.lhs_identifier.0 @@ -130,6 +165,26 @@ fn name_from_id_or_string(nm: &weedle::attribute::IdentifierOrString<'_>) -> Str } } +fn rust_kind_from_id_or_string(nm: &weedle::attribute::IdentifierOrString<'_>) -> Result<RustKind> { + Ok(match nm { + weedle::attribute::IdentifierOrString::String(str_lit) => match str_lit.0 { + // support names which match either procmacro or udl + "interface" => RustKind::Object, + "object" => RustKind::Object, + "record" => RustKind::Record, + "dictionary" => RustKind::Record, + "enum" => RustKind::Enum, + "trait" => RustKind::Trait, + "callback" => RustKind::CallbackInterface, + "trait_with_foreign" => RustKind::CallbackTrait, + _ => anyhow::bail!("Unknown `[Rust=]` kind {:?}", str_lit.0), + }, + weedle::attribute::IdentifierOrString::Identifier(_) => { + anyhow::bail!("Expected string attribute value, got identifier") + } + }) +} + /// Parse a weedle `ExtendedAttributeList` into a list of `Attribute`s, /// erroring out on duplicates. fn parse_attributes<F>( @@ -161,7 +216,6 @@ where } /// Attributes that can be attached to an `enum` definition in the UDL. -/// There's only one case here: using `[Error]` to mark an enum as an error class. #[derive(Debug, Clone, Checksum, Default)] pub(super) struct EnumAttributes(Vec<Attribute>); @@ -169,6 +223,12 @@ impl EnumAttributes { pub fn contains_error_attr(&self) -> bool { self.0.iter().any(|attr| attr.is_error()) } + + pub fn contains_non_exhaustive_attr(&self) -> bool { + self.0 + .iter() + .any(|attr| matches!(attr, Attribute::NonExhaustive)) + } } impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for EnumAttributes { @@ -178,6 +238,10 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for EnumAttributes { ) -> Result<Self, Self::Error> { let attrs = parse_attributes(weedle_attributes, |attr| match attr { Attribute::Error => Ok(()), + Attribute::NonExhaustive => Ok(()), + // Allow `[Enum]`, since we may be parsing an attribute list from an interface with the + // `[Enum]` attribute. + Attribute::Enum => Ok(()), _ => bail!(format!("{attr:?} not supported for enums")), })?; Ok(Self(attrs)) @@ -196,8 +260,9 @@ impl<T: TryInto<EnumAttributes, Error = anyhow::Error>> TryFrom<Option<T>> for E /// Represents UDL attributes that might appear on a function. /// -/// This supports the `[Throws=ErrorName]` attribute for functions that -/// can produce an error. +/// This supports: +/// * `[Throws=ErrorName]` attribute for functions that can produce an error. +/// * `[Async] for async functions #[derive(Debug, Clone, Checksum, Default)] pub(super) struct FunctionAttributes(Vec<Attribute>); @@ -210,6 +275,10 @@ impl FunctionAttributes { _ => None, }) } + + pub(super) fn is_async(&self) -> bool { + self.0.iter().any(|attr| matches!(attr, Attribute::Async)) + } } impl FromIterator<Attribute> for FunctionAttributes { @@ -224,7 +293,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for FunctionAttribut weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>, ) -> Result<Self, Self::Error> { let attrs = parse_attributes(weedle_attributes, |attr| match attr { - Attribute::Throws(_) => Ok(()), + Attribute::Throws(_) | Attribute::Async => Ok(()), _ => bail!(format!("{attr:?} not supported for functions")), })?; Ok(Self(attrs)) @@ -294,12 +363,25 @@ impl InterfaceAttributes { self.0.iter().any(|attr| attr.is_error()) } - pub fn object_impl(&self) -> ObjectImpl { - if self.0.iter().any(|attr| matches!(attr, Attribute::Trait)) { - ObjectImpl::Trait - } else { - ObjectImpl::Struct - } + pub fn contains_trait(&self) -> bool { + self.0.iter().any(|attr| matches!(attr, Attribute::Trait)) + } + + pub fn contains_with_foreign(&self) -> bool { + self.0 + .iter() + .any(|attr| matches!(attr, Attribute::WithForeign)) + } + + pub fn object_impl(&self) -> Result<ObjectImpl> { + Ok( + match (self.contains_trait(), self.contains_with_foreign()) { + (true, true) => ObjectImpl::CallbackTrait, + (true, false) => ObjectImpl::Trait, + (false, false) => ObjectImpl::Struct, + (false, true) => bail!("WithForeign can't be specified without Trait"), + }, + ) } pub fn get_traits(&self) -> Vec<String> { self.0 @@ -321,6 +403,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for InterfaceAttribu Attribute::Enum => Ok(()), Attribute::Error => Ok(()), Attribute::Trait => Ok(()), + Attribute::WithForeign => Ok(()), Attribute::Traits(_) => Ok(()), _ => bail!(format!("{attr:?} not supported for interface definition")), })?; @@ -373,6 +456,10 @@ impl ConstructorAttributes { _ => None, }) } + + pub(super) fn is_async(&self) -> bool { + self.0.iter().any(|attr| matches!(attr, Attribute::Async)) + } } impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ConstructorAttributes { @@ -383,6 +470,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ConstructorAttri let attrs = parse_attributes(weedle_attributes, |attr| match attr { Attribute::Throws(_) => Ok(()), Attribute::Name(_) => Ok(()), + Attribute::Async => Ok(()), _ => bail!(format!("{attr:?} not supported for constructors")), })?; Ok(Self(attrs)) @@ -406,6 +494,10 @@ impl MethodAttributes { }) } + pub(super) fn is_async(&self) -> bool { + self.0.iter().any(|attr| matches!(attr, Attribute::Async)) + } + pub(super) fn get_self_by_arc(&self) -> bool { self.0 .iter() @@ -425,8 +517,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for MethodAttributes weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>, ) -> Result<Self, Self::Error> { let attrs = parse_attributes(weedle_attributes, |attr| match attr { - Attribute::SelfType(_) => Ok(()), - Attribute::Throws(_) => Ok(()), + Attribute::SelfType(_) | Attribute::Throws(_) | Attribute::Async => Ok(()), _ => bail!(format!("{attr:?} not supported for methods")), })?; Ok(Self(attrs)) @@ -498,10 +589,18 @@ impl TypedefAttributes { }) } + pub(super) fn rust_kind(&self) -> Option<RustKind> { + self.0.iter().find_map(|attr| match attr { + Attribute::Rust { kind, .. } => Some(*kind), + _ => None, + }) + } + pub(super) fn external_tagged(&self) -> Option<bool> { // If it was "exported" via a proc-macro the FfiConverter was not tagged. self.0.iter().find_map(|attr| match attr { Attribute::External { export, .. } => Some(!*export), + Attribute::Rust { .. } => Some(false), _ => None, }) } @@ -513,7 +612,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for TypedefAttribute weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>, ) -> Result<Self, Self::Error> { let attrs = parse_attributes(weedle_attributes, |attr| match attr { - Attribute::External { .. } | Attribute::Custom => Ok(()), + Attribute::External { .. } | Attribute::Custom | Attribute::Rust { .. } => Ok(()), _ => bail!(format!("{attr:?} not supported for typedefs")), })?; Ok(Self(attrs)) @@ -641,14 +740,22 @@ mod test { } #[test] - fn test_throws_attribute() { + fn test_function_attributes() { let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap(); let attrs = FunctionAttributes::try_from(&node).unwrap(); assert!(matches!(attrs.get_throws_err(), Some("Error"))); + assert!(!attrs.is_async()); let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap(); let attrs = FunctionAttributes::try_from(&node).unwrap(); assert!(attrs.get_throws_err().is_none()); + assert!(!attrs.is_async()); + + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Async]").unwrap(); + let attrs = FunctionAttributes::try_from(&node).unwrap(); + assert!(matches!(attrs.get_throws_err(), Some("Error"))); + assert!(attrs.is_async()); } #[test] @@ -673,22 +780,34 @@ mod test { let attrs = MethodAttributes::try_from(&node).unwrap(); assert!(!attrs.get_self_by_arc()); assert!(matches!(attrs.get_throws_err(), Some("Error"))); + assert!(!attrs.is_async()); let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap(); let attrs = MethodAttributes::try_from(&node).unwrap(); assert!(!attrs.get_self_by_arc()); assert!(attrs.get_throws_err().is_none()); + assert!(!attrs.is_async()); let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc, Throws=Error]").unwrap(); let attrs = MethodAttributes::try_from(&node).unwrap(); assert!(attrs.get_self_by_arc()); assert!(attrs.get_throws_err().is_some()); + assert!(!attrs.is_async()); + + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc, Throws=Error, Async]") + .unwrap(); + let attrs = MethodAttributes::try_from(&node).unwrap(); + assert!(attrs.get_self_by_arc()); + assert!(attrs.get_throws_err().is_some()); + assert!(attrs.is_async()); let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc]").unwrap(); let attrs = MethodAttributes::try_from(&node).unwrap(); assert!(attrs.get_self_by_arc()); assert!(attrs.get_throws_err().is_none()); + assert!(!attrs.is_async()); } #[test] @@ -710,6 +829,11 @@ mod test { let attrs = ConstructorAttributes::try_from(&node).unwrap(); assert!(matches!(attrs.get_throws_err(), Some("Error"))); assert!(matches!(attrs.get_name(), Some("MyFactory"))); + assert!(!attrs.is_async()); + + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Async]").unwrap(); + let attrs = ConstructorAttributes::try_from(&node).unwrap(); + assert!(attrs.is_async()); } #[test] @@ -754,15 +878,24 @@ mod test { fn test_trait_attribute() { let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap(); let attrs = InterfaceAttributes::try_from(&node).unwrap(); - assert_eq!(attrs.object_impl(), ObjectImpl::Trait); + assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::Trait); + + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Trait, WithForeign]").unwrap(); + let attrs = InterfaceAttributes::try_from(&node).unwrap(); + assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::CallbackTrait); let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap(); let attrs = InterfaceAttributes::try_from(&node).unwrap(); - assert_eq!(attrs.object_impl(), ObjectImpl::Struct); + assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::Struct); + + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[WithForeign]").unwrap(); + let attrs = InterfaceAttributes::try_from(&node).unwrap(); + assert!(attrs.object_impl().is_err()) } #[test] - fn test_enum_attribute() { + fn test_enum_attribute_on_interface() { let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap(); let attrs = InterfaceAttributes::try_from(&node).unwrap(); assert!(matches!(attrs.contains_enum_attr(), true)); @@ -783,6 +916,38 @@ mod test { ); } + // Test parsing attributes for enum definitions + #[test] + fn test_enum_attributes() { + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Error, NonExhaustive]").unwrap(); + let attrs = EnumAttributes::try_from(&node).unwrap(); + assert!(attrs.contains_error_attr()); + assert!(attrs.contains_non_exhaustive_attr()); + + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap(); + let err = EnumAttributes::try_from(&node).unwrap_err(); + assert_eq!(err.to_string(), "Trait not supported for enums"); + } + + // Test parsing attributes for interface definitions with the `[Enum]` attribute + #[test] + fn test_enum_attributes_from_interface() { + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap(); + assert!(EnumAttributes::try_from(&node).is_ok()); + + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Enum, Error, NonExhaustive]") + .unwrap(); + let attrs = EnumAttributes::try_from(&node).unwrap(); + assert!(attrs.contains_error_attr()); + assert!(attrs.contains_non_exhaustive_attr()); + + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum, Trait]").unwrap(); + let err = EnumAttributes::try_from(&node).unwrap_err(); + assert_eq!(err.to_string(), "Trait not supported for enums"); + } + #[test] fn test_other_attributes_not_supported_for_interfaces() { let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait, ByRef]").unwrap(); diff --git a/third_party/rust/uniffi_udl/src/collectors.rs b/third_party/rust/uniffi_udl/src/collectors.rs index 6a91ab4a93..de5489f5f9 100644 --- a/third_party/rust/uniffi_udl/src/collectors.rs +++ b/third_party/rust/uniffi_udl/src/collectors.rs @@ -5,7 +5,7 @@ //! # Collects metadata from UDL. use crate::attributes; -use crate::converters::APIConverter; +use crate::converters::{convert_docstring, APIConverter}; use crate::finder; use crate::resolver::TypeResolver; use anyhow::{bail, Result}; @@ -138,6 +138,7 @@ impl From<InterfaceCollector> for uniffi_meta::MetadataGroup { crate_name: value.types.module_path(), name: value.types.namespace, }, + namespace_docstring: value.types.namespace_docstring.clone(), items: value.items, } } @@ -171,15 +172,13 @@ impl APIBuilder for weedle::Definition<'_> { match self { weedle::Definition::Namespace(d) => d.process(ci)?, weedle::Definition::Enum(d) => { + let mut e: uniffi_meta::EnumMetadata = d.convert(ci)?; // We check if the enum represents an error... let attrs = attributes::EnumAttributes::try_from(d.attributes.as_ref())?; if attrs.contains_error_attr() { - let e: uniffi_meta::ErrorMetadata = d.convert(ci)?; - ci.add_definition(e.into())?; - } else { - let e: uniffi_meta::EnumMetadata = d.convert(ci)?; - ci.add_definition(e.into())?; + e.forced_flatness = Some(true); } + ci.add_definition(e.into())?; } weedle::Definition::Dictionary(d) => { let rec = d.convert(ci)?; @@ -187,12 +186,9 @@ impl APIBuilder for weedle::Definition<'_> { } weedle::Definition::Interface(d) => { let attrs = attributes::InterfaceAttributes::try_from(d.attributes.as_ref())?; - if attrs.contains_enum_attr() { + if attrs.contains_enum_attr() || attrs.contains_error_attr() { let e: uniffi_meta::EnumMetadata = d.convert(ci)?; ci.add_definition(e.into())?; - } else if attrs.contains_error_attr() { - let e: uniffi_meta::ErrorMetadata = d.convert(ci)?; - ci.add_definition(e.into())?; } else { let obj: uniffi_meta::ObjectMetadata = d.convert(ci)?; ci.add_definition(obj.into())?; @@ -218,6 +214,7 @@ impl APIBuilder for weedle::NamespaceDefinition<'_> { if self.identifier.0 != ci.types.namespace { bail!("duplicate namespace definition"); } + ci.types.namespace_docstring = self.docstring.as_ref().map(|v| convert_docstring(&v.0)); for func in self.members.body.convert(ci)? { ci.add_definition(func.into())?; } @@ -229,6 +226,7 @@ impl APIBuilder for weedle::NamespaceDefinition<'_> { pub(crate) struct TypeCollector { /// The unique prefix that we'll use for namespacing when exposing this component's API. pub namespace: String, + pub namespace_docstring: Option<String>, pub crate_name: String, diff --git a/third_party/rust/uniffi_udl/src/converters/callables.rs b/third_party/rust/uniffi_udl/src/converters/callables.rs index 3e15bb8e02..dda3c3a3ce 100644 --- a/third_party/rust/uniffi_udl/src/converters/callables.rs +++ b/third_party/rust/uniffi_udl/src/converters/callables.rs @@ -5,6 +5,7 @@ use super::APIConverter; use crate::attributes::ArgumentAttributes; use crate::attributes::{ConstructorAttributes, FunctionAttributes, MethodAttributes}; +use crate::converters::convert_docstring; use crate::literal::convert_default_value; use crate::InterfaceCollector; use anyhow::{bail, Result}; @@ -41,6 +42,7 @@ impl APIConverter<FieldMetadata> for weedle::argument::SingleArgument<'_> { name: self.identifier.0.to_string(), ty: type_, default: None, + docstring: None, }) } } @@ -89,6 +91,7 @@ impl APIConverter<FnMetadata> for weedle::namespace::OperationNamespaceMember<'_ Some(id) => id.0.to_string(), }; let attrs = FunctionAttributes::try_from(self.attributes.as_ref())?; + let is_async = attrs.is_async(); let throws = match attrs.get_throws_err() { None => None, Some(name) => match ci.get_type(name) { @@ -99,10 +102,11 @@ impl APIConverter<FnMetadata> for weedle::namespace::OperationNamespaceMember<'_ Ok(FnMetadata { module_path: ci.module_path(), name, - is_async: false, + is_async, return_type, inputs: self.args.body.list.convert(ci)?, throws, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), checksum: None, }) } @@ -122,10 +126,12 @@ impl APIConverter<ConstructorMetadata> for weedle::interface::ConstructorInterfa name: String::from(attributes.get_name().unwrap_or("new")), // We don't know the name of the containing `Object` at this point, fill it in later. self_name: Default::default(), + is_async: attributes.is_async(), // Also fill in checksum_fn_name later, since it depends on object_name inputs: self.args.body.list.convert(ci)?, throws, checksum: None, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -140,6 +146,7 @@ impl APIConverter<MethodMetadata> for weedle::interface::OperationInterfaceMembe } let return_type = ci.resolve_return_type_expression(&self.return_type)?; let attributes = MethodAttributes::try_from(self.attributes.as_ref())?; + let is_async = attributes.is_async(); let throws = match attributes.get_throws_err() { Some(name) => match ci.get_type(name) { @@ -164,12 +171,13 @@ impl APIConverter<MethodMetadata> for weedle::interface::OperationInterfaceMembe }, // We don't know the name of the containing `Object` at this point, fill it in later. self_name: Default::default(), - is_async: false, // not supported in UDL + is_async, inputs: self.args.body.list.convert(ci)?, return_type, throws, takes_self_by_arc, checksum: None, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -184,6 +192,7 @@ impl APIConverter<TraitMethodMetadata> for weedle::interface::OperationInterface } let return_type = ci.resolve_return_type_expression(&self.return_type)?; let attributes = MethodAttributes::try_from(self.attributes.as_ref())?; + let is_async = attributes.is_async(); let throws = match attributes.get_throws_err() { Some(name) => match ci.get_type(name) { @@ -208,12 +217,13 @@ impl APIConverter<TraitMethodMetadata> for weedle::interface::OperationInterface name } }, - is_async: false, // not supported in udl + is_async, inputs: self.args.body.list.convert(ci)?, return_type, throws, takes_self_by_arc, checksum: None, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } diff --git a/third_party/rust/uniffi_udl/src/converters/enum_.rs b/third_party/rust/uniffi_udl/src/converters/enum_.rs index a3e68fd23e..1615a1a7ca 100644 --- a/third_party/rust/uniffi_udl/src/converters/enum_.rs +++ b/third_party/rust/uniffi_udl/src/converters/enum_.rs @@ -3,19 +3,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::APIConverter; -use crate::InterfaceCollector; +use crate::{attributes::EnumAttributes, converters::convert_docstring, InterfaceCollector}; use anyhow::{bail, Result}; -use uniffi_meta::{EnumMetadata, ErrorMetadata, VariantMetadata}; +use uniffi_meta::{EnumMetadata, VariantMetadata}; -// Note that we have four `APIConverter` impls here - one for the `enum` case, -// one for the `[Error] enum` case, and and one for the `[Enum] interface` case, -// and one for the `[Error] interface` case. +// Note that we have 2 `APIConverter` impls here - one for the `enum` case +// (including an enum with `[Error]`), and one for the `[Error] interface` cas +// (which is still an enum, but with different "flatness" characteristics.) impl APIConverter<EnumMetadata> for weedle::EnumDefinition<'_> { fn convert(&self, ci: &mut InterfaceCollector) -> Result<EnumMetadata> { + let attributes = EnumAttributes::try_from(self.attributes.as_ref())?; Ok(EnumMetadata { module_path: ci.module_path(), name: self.identifier.0.to_string(), + forced_flatness: None, + discr_type: None, variants: self .values .body @@ -23,35 +26,15 @@ impl APIConverter<EnumMetadata> for weedle::EnumDefinition<'_> { .iter() .map::<Result<_>, _>(|v| { Ok(VariantMetadata { - name: v.0.to_string(), + name: v.value.0.to_string(), + discr: None, fields: vec![], + docstring: v.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) }) .collect::<Result<Vec<_>>>()?, - }) - } -} - -impl APIConverter<ErrorMetadata> for weedle::EnumDefinition<'_> { - fn convert(&self, ci: &mut InterfaceCollector) -> Result<ErrorMetadata> { - Ok(ErrorMetadata::Enum { - enum_: EnumMetadata { - module_path: ci.module_path(), - name: self.identifier.0.to_string(), - variants: self - .values - .body - .list - .iter() - .map::<Result<_>, _>(|v| { - Ok(VariantMetadata { - name: v.0.to_string(), - fields: vec![], - }) - }) - .collect::<Result<Vec<_>>>()?, - }, - is_flat: true, + non_exhaustive: attributes.contains_non_exhaustive_attr(), + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -61,11 +44,11 @@ impl APIConverter<EnumMetadata> for weedle::InterfaceDefinition<'_> { if self.inheritance.is_some() { bail!("interface inheritance is not supported for enum interfaces"); } - // We don't need to check `self.attributes` here; if calling code has dispatched - // to this impl then we already know there was an `[Enum]` attribute. + let attributes = EnumAttributes::try_from(self.attributes.as_ref())?; Ok(EnumMetadata { module_path: ci.module_path(), name: self.identifier.0.to_string(), + forced_flatness: Some(false), variants: self .members .body @@ -78,41 +61,15 @@ impl APIConverter<EnumMetadata> for weedle::InterfaceDefinition<'_> { ), }) .collect::<Result<Vec<_>>>()?, + discr_type: None, + non_exhaustive: attributes.contains_non_exhaustive_attr(), + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), // Enums declared using the `[Enum] interface` syntax might have variants with fields. //flat: false, }) } } -impl APIConverter<ErrorMetadata> for weedle::InterfaceDefinition<'_> { - fn convert(&self, ci: &mut InterfaceCollector) -> Result<ErrorMetadata> { - if self.inheritance.is_some() { - bail!("interface inheritance is not supported for enum interfaces"); - } - // We don't need to check `self.attributes` here; callers have already checked them - // to work out which version to dispatch to. - Ok(ErrorMetadata::Enum { - enum_: EnumMetadata { - module_path: ci.module_path(), - name: self.identifier.0.to_string(), - variants: self - .members - .body - .iter() - .map::<Result<VariantMetadata>, _>(|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<_>>>()?, - }, - is_flat: false, - }) - } -} - #[cfg(test)] mod test { use super::*; diff --git a/third_party/rust/uniffi_udl/src/converters/interface.rs b/third_party/rust/uniffi_udl/src/converters/interface.rs index 58e6a9c8a0..ef9bdd9540 100644 --- a/third_party/rust/uniffi_udl/src/converters/interface.rs +++ b/third_party/rust/uniffi_udl/src/converters/interface.rs @@ -4,7 +4,7 @@ use super::APIConverter; use crate::attributes::InterfaceAttributes; -use crate::InterfaceCollector; +use crate::{converters::convert_docstring, InterfaceCollector}; use anyhow::{bail, Result}; use std::collections::HashSet; use uniffi_meta::{ @@ -23,7 +23,7 @@ impl APIConverter<ObjectMetadata> for weedle::InterfaceDefinition<'_> { }; let object_name = self.identifier.0; - let object_impl = attributes.object_impl(); + let object_impl = attributes.object_impl()?; // Convert each member into a constructor or method, guarding against duplicate names. // They get added to the ci and aren't carried in ObjectMetadata. let mut member_names = HashSet::new(); @@ -70,6 +70,7 @@ impl APIConverter<ObjectMetadata> for weedle::InterfaceDefinition<'_> { throws: None, takes_self_by_arc: false, checksum: None, + docstring: None, }) }; // Trait methods are in the Metadata. @@ -130,6 +131,7 @@ impl APIConverter<ObjectMetadata> for weedle::InterfaceDefinition<'_> { module_path: ci.module_path(), name: object_name.to_string(), imp: object_impl, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } diff --git a/third_party/rust/uniffi_udl/src/converters/mod.rs b/third_party/rust/uniffi_udl/src/converters/mod.rs index 7a2d22ac42..195d9cc0b7 100644 --- a/third_party/rust/uniffi_udl/src/converters/mod.rs +++ b/third_party/rust/uniffi_udl/src/converters/mod.rs @@ -29,6 +29,11 @@ pub(crate) trait APIConverter<T> { fn convert(&self, ci: &mut InterfaceCollector) -> Result<T>; } +// Convert UDL docstring into metadata docstring +pub(crate) fn convert_docstring(docstring: &str) -> String { + textwrap::dedent(docstring) +} + /// Convert a list of weedle items into a list of `InterfaceCollector` items, /// by doing a direct item-by-item mapping. impl<U, T: APIConverter<U>> APIConverter<Vec<U>> for Vec<T> { @@ -72,6 +77,7 @@ impl APIConverter<VariantMetadata> for weedle::interface::OperationInterfaceMemb }; Ok(VariantMetadata { name, + discr: None, fields: self .args .body @@ -79,6 +85,7 @@ impl APIConverter<VariantMetadata> for weedle::interface::OperationInterfaceMemb .iter() .map(|arg| arg.convert(ci)) .collect::<Result<Vec<_>>>()?, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -95,6 +102,7 @@ impl APIConverter<RecordMetadata> for weedle::DictionaryDefinition<'_> { module_path: ci.module_path(), name: self.identifier.0.to_string(), fields: self.members.body.convert(ci)?, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -113,6 +121,7 @@ impl APIConverter<FieldMetadata> for weedle::dictionary::DictionaryMember<'_> { name: self.identifier.0.to_string(), ty: type_, default, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -150,6 +159,7 @@ impl APIConverter<CallbackInterfaceMetadata> for weedle::CallbackInterfaceDefini Ok(CallbackInterfaceMetadata { module_path: ci.module_path(), name: object_name.to_string(), + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } diff --git a/third_party/rust/uniffi_udl/src/finder.rs b/third_party/rust/uniffi_udl/src/finder.rs index 0c4c187dc0..259557ad07 100644 --- a/third_party/rust/uniffi_udl/src/finder.rs +++ b/third_party/rust/uniffi_udl/src/finder.rs @@ -22,8 +22,8 @@ use std::convert::TryFrom; use anyhow::{bail, Result}; use super::TypeCollector; -use crate::attributes::{InterfaceAttributes, TypedefAttributes}; -use uniffi_meta::Type; +use crate::attributes::{InterfaceAttributes, RustKind, TypedefAttributes}; +use uniffi_meta::{ObjectImpl, Type}; /// Trait to help with an early "type discovery" phase when processing the UDL. /// @@ -75,7 +75,7 @@ impl TypeFinder for weedle::InterfaceDefinition<'_> { Type::Object { name, module_path: types.module_path(), - imp: attrs.object_impl(), + imp: attrs.object_impl()?, }, ) } @@ -111,7 +111,6 @@ impl TypeFinder for weedle::EnumDefinition<'_> { impl TypeFinder for weedle::TypedefDefinition<'_> { fn add_type_definitions_to(&self, types: &mut TypeCollector) -> Result<()> { - let name = self.identifier.0; let attrs = TypedefAttributes::try_from(self.attributes.as_ref())?; // If we wanted simple `typedef`s, it would be as easy as: // > let t = types.resolve_type_expression(&self.type_)?; @@ -122,29 +121,52 @@ impl TypeFinder for weedle::TypedefDefinition<'_> { // `FfiConverter` implementation. let builtin = types.resolve_type_expression(&self.type_)?; types.add_type_definition( - name, + self.identifier.0, Type::Custom { module_path: types.module_path(), - name: name.to_string(), + name: self.identifier.0.to_string(), builtin: builtin.into(), }, ) } else { - let kind = attrs.external_kind().expect("External missing"); - let tagged = attrs.external_tagged().expect("External missing"); + let module_path = types.module_path(); + let name = self.identifier.0.to_string(); + let ty = match attrs.rust_kind() { + Some(RustKind::Object) => Type::Object { + module_path, + name, + imp: ObjectImpl::Struct, + }, + Some(RustKind::Trait) => Type::Object { + module_path, + name, + imp: ObjectImpl::Trait, + }, + Some(RustKind::CallbackTrait) => Type::Object { + module_path, + name, + imp: ObjectImpl::CallbackTrait, + }, + Some(RustKind::Record) => Type::Record { module_path, name }, + Some(RustKind::Enum) => Type::Enum { module_path, name }, + Some(RustKind::CallbackInterface) => Type::CallbackInterface { module_path, name }, + // must be external + None => { + let kind = attrs.external_kind().expect("External missing kind"); + let tagged = attrs.external_tagged().expect("External missing tagged"); + Type::External { + name, + namespace: "".to_string(), // we don't know this yet + module_path: attrs.get_crate_name(), + kind, + tagged, + } + } + }; // A crate which can supply an `FfiConverter`. // We don't reference `self._type`, so ideally we could insist on it being // the literal 'extern' but that's tricky - types.add_type_definition( - name, - Type::External { - name: name.to_string(), - namespace: "".to_string(), // we don't know this yet - module_path: attrs.get_crate_name(), - kind, - tagged, - }, - ) + types.add_type_definition(self.identifier.0, ty) } } } @@ -152,7 +174,7 @@ impl TypeFinder for weedle::TypedefDefinition<'_> { impl TypeFinder for weedle::CallbackInterfaceDefinition<'_> { fn add_type_definitions_to(&self, types: &mut TypeCollector) -> Result<()> { if self.attributes.is_some() { - bail!("no typedef attributes are currently supported"); + bail!("no callback interface attributes are currently supported"); } let name = self.identifier.0.to_string(); types.add_type_definition( diff --git a/third_party/rust/uniffi_udl/src/lib.rs b/third_party/rust/uniffi_udl/src/lib.rs index 5e6e72a7f7..a9dad5d6c5 100644 --- a/third_party/rust/uniffi_udl/src/lib.rs +++ b/third_party/rust/uniffi_udl/src/lib.rs @@ -6,7 +6,7 @@ //! //! This library is dedicated to parsing a string in a webidl syntax, as described by //! weedle and with our own custom take on the attributes etc, pushing the boundaries -//! of that syntax to describe a uniffi `MetatadataGroup`. +//! of that syntax to describe a uniffi `MetadataGroup`. //! //! The output of this module is consumed by uniffi_bindgen to generate stuff. diff --git a/third_party/rust/uniffi_udl/src/literal.rs b/third_party/rust/uniffi_udl/src/literal.rs index 78f2544254..5fbf022644 100644 --- a/third_party/rust/uniffi_udl/src/literal.rs +++ b/third_party/rust/uniffi_udl/src/literal.rs @@ -84,8 +84,10 @@ pub(super) fn convert_default_value( (weedle::literal::DefaultValue::String(s), Type::Enum { .. }) => { Literal::Enum(s.0.to_string(), type_.clone()) } - (weedle::literal::DefaultValue::Null(_), Type::Optional { .. }) => Literal::Null, - (_, Type::Optional { inner_type, .. }) => convert_default_value(default_value, inner_type)?, + (weedle::literal::DefaultValue::Null(_), Type::Optional { .. }) => Literal::None, + (_, Type::Optional { inner_type, .. }) => Literal::Some { + inner: Box::new(convert_default_value(default_value, inner_type)?), + }, // We'll ensure the type safety in the convert_* number methods. (weedle::literal::DefaultValue::Integer(i), _) => convert_integer(i, type_)?, @@ -144,7 +146,7 @@ mod test { inner_type: Box::new(Type::String) } )?, - Literal::Null + Literal::None )); Ok(()) } diff --git a/third_party/rust/uniffi_udl/src/resolver.rs b/third_party/rust/uniffi_udl/src/resolver.rs index 14a7a4c6f1..ea98cd7a99 100644 --- a/third_party/rust/uniffi_udl/src/resolver.rs +++ b/third_party/rust/uniffi_udl/src/resolver.rs @@ -209,7 +209,6 @@ pub(crate) fn resolve_builtin_type(name: &str) -> Option<Type> { "f64" => Some(Type::Float64), "timestamp" => Some(Type::Timestamp), "duration" => Some(Type::Duration), - "ForeignExecutor" => Some(Type::ForeignExecutor), _ => None, } } |