summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_udl/src/finder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi_udl/src/finder.rs')
-rw-r--r--third_party/rust/uniffi_udl/src/finder.rs285
1 files changed, 285 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_udl/src/finder.rs b/third_party/rust/uniffi_udl/src/finder.rs
new file mode 100644
index 0000000000..0c4c187dc0
--- /dev/null
+++ b/third_party/rust/uniffi_udl/src/finder.rs
@@ -0,0 +1,285 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Helpers for finding the named types defined in a UDL interface.
+//!
+//! This module provides the [`TypeFinder`] trait, an abstraction for walking
+//! the weedle parse tree, looking for type definitions, and accumulating them
+//! in a [`TypeCollector`].
+//!
+//! The type-finding process only discovers very basic information about names
+//! and their corresponding types. For example, it can discover that "Foobar"
+//! names a Record, but it won't discover anything about the fields of that
+//! record.
+//!
+//! Factoring this functionality out into a separate phase makes the subsequent
+//! work of more *detailed* parsing of the UDL a lot simpler, we know how to resolve
+//! names to types when building up the full interface definition.
+
+use std::convert::TryFrom;
+
+use anyhow::{bail, Result};
+
+use super::TypeCollector;
+use crate::attributes::{InterfaceAttributes, TypedefAttributes};
+use uniffi_meta::Type;
+
+/// Trait to help with an early "type discovery" phase when processing the UDL.
+///
+/// This trait does structural matching against weedle AST nodes from a parsed
+/// UDL file, looking for all the newly-defined types in the file and accumulating
+/// them in the given `TypeCollector`.
+pub(crate) trait TypeFinder {
+ fn add_type_definitions_to(&self, types: &mut TypeCollector) -> Result<()>;
+}
+
+impl<T: TypeFinder> TypeFinder for &[T] {
+ fn add_type_definitions_to(&self, types: &mut TypeCollector) -> Result<()> {
+ for item in *self {
+ item.add_type_definitions_to(types)?;
+ }
+ Ok(())
+ }
+}
+
+impl TypeFinder for weedle::Definition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeCollector) -> Result<()> {
+ match self {
+ weedle::Definition::Interface(d) => d.add_type_definitions_to(types),
+ weedle::Definition::Dictionary(d) => d.add_type_definitions_to(types),
+ weedle::Definition::Enum(d) => d.add_type_definitions_to(types),
+ weedle::Definition::Typedef(d) => d.add_type_definitions_to(types),
+ weedle::Definition::CallbackInterface(d) => d.add_type_definitions_to(types),
+ _ => Ok(()),
+ }
+ }
+}
+
+impl TypeFinder for weedle::InterfaceDefinition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeCollector) -> Result<()> {
+ let name = self.identifier.0.to_string();
+ let attrs = InterfaceAttributes::try_from(self.attributes.as_ref())?;
+ // Some enum types are defined using an `interface` with a special attribute.
+ if attrs.contains_enum_attr() || attrs.contains_error_attr() {
+ types.add_type_definition(
+ self.identifier.0,
+ Type::Enum {
+ name,
+ module_path: types.module_path(),
+ },
+ )
+ } else {
+ types.add_type_definition(
+ self.identifier.0,
+ Type::Object {
+ name,
+ module_path: types.module_path(),
+ imp: attrs.object_impl(),
+ },
+ )
+ }
+ }
+}
+
+impl TypeFinder for weedle::DictionaryDefinition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeCollector) -> Result<()> {
+ let name = self.identifier.0.to_string();
+ types.add_type_definition(
+ self.identifier.0,
+ Type::Record {
+ name,
+ module_path: types.module_path(),
+ },
+ )
+ }
+}
+
+impl TypeFinder for weedle::EnumDefinition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeCollector) -> Result<()> {
+ let name = self.identifier.0.to_string();
+ // Our error types are defined using an `enum` with a special attribute.
+ types.add_type_definition(
+ self.identifier.0,
+ Type::Enum {
+ name,
+ module_path: types.module_path(),
+ },
+ )
+ }
+}
+
+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_)?;
+ // > types.add_type_definition(name, t)
+ // But we don't - `typedef`s are reserved for external types.
+ if attrs.is_custom() {
+ // A local type which wraps a builtin and for which we will generate an
+ // `FfiConverter` implementation.
+ let builtin = types.resolve_type_expression(&self.type_)?;
+ types.add_type_definition(
+ name,
+ Type::Custom {
+ module_path: types.module_path(),
+ name: name.to_string(),
+ builtin: builtin.into(),
+ },
+ )
+ } else {
+ let kind = attrs.external_kind().expect("External missing");
+ let tagged = attrs.external_tagged().expect("External missing");
+ // 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,
+ },
+ )
+ }
+ }
+}
+
+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");
+ }
+ let name = self.identifier.0.to_string();
+ types.add_type_definition(
+ self.identifier.0,
+ Type::CallbackInterface {
+ name,
+ module_path: types.module_path(),
+ },
+ )
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use uniffi_meta::ExternalKind;
+
+ // A helper to take valid UDL and a closure to check what's in it.
+ fn test_a_finding<F>(udl: &str, tester: F)
+ where
+ F: FnOnce(TypeCollector),
+ {
+ let idl = weedle::parse(udl).unwrap();
+ let mut types = TypeCollector::default();
+ types.add_type_definitions_from(idl.as_ref()).unwrap();
+ tester(types);
+ }
+
+ #[test]
+ fn test_type_finding() {
+ test_a_finding(
+ r#"
+ callback interface TestCallbacks {
+ string hello(u32 count);
+ };
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("TestCallbacks").unwrap(), Type::CallbackInterface { name, .. } if name == "TestCallbacks")
+ );
+ },
+ );
+
+ test_a_finding(
+ r#"
+ dictionary TestRecord {
+ u32 field;
+ };
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("TestRecord").unwrap(), Type::Record { name, .. } if name == "TestRecord")
+ );
+ },
+ );
+
+ test_a_finding(
+ r#"
+ enum TestItems { "one", "two" };
+
+ [Error]
+ enum TestError { "ErrorOne", "ErrorTwo" };
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("TestItems").unwrap(), Type::Enum { name, .. } if name == "TestItems")
+ );
+ assert!(
+ matches!(types.get_type_definition("TestError").unwrap(), Type::Enum { name, .. } if name == "TestError")
+ );
+ },
+ );
+
+ test_a_finding(
+ r#"
+ interface TestObject {
+ constructor();
+ };
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("TestObject").unwrap(), Type::Object{ name, .. } if name == "TestObject")
+ );
+ },
+ );
+
+ test_a_finding(
+ r#"
+ [External="crate-name"]
+ typedef extern ExternalType;
+
+ [ExternalInterface="crate-name"]
+ typedef extern ExternalInterfaceType;
+
+ [Custom]
+ typedef string CustomType;
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("ExternalType").unwrap(), Type::External { name, module_path, kind: ExternalKind::DataClass, .. }
+ if name == "ExternalType" && module_path == "crate-name")
+ );
+ assert!(
+ matches!(types.get_type_definition("ExternalInterfaceType").unwrap(), Type::External { name, module_path, kind: ExternalKind::Interface, .. }
+ if name == "ExternalInterfaceType" && module_path == "crate-name")
+ );
+ assert!(
+ matches!(types.get_type_definition("CustomType").unwrap(), Type::Custom { name, builtin, ..}
+ if name == "CustomType" && *builtin == Type::String)
+ );
+ },
+ );
+ }
+
+ fn get_err(udl: &str) -> String {
+ let parsed = weedle::parse(udl).unwrap();
+ let mut types = TypeCollector::default();
+ let err = types
+ .add_type_definitions_from(parsed.as_ref())
+ .unwrap_err();
+ err.to_string()
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_typedef_error_on_no_attr() {
+ // Sorry, still working out what we want for non-imported typedefs..
+ get_err("typedef string Custom;");
+ }
+}