diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/uniffi_udl/src/resolver.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/uniffi_udl/src/resolver.rs')
-rw-r--r-- | third_party/rust/uniffi_udl/src/resolver.rs | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_udl/src/resolver.rs b/third_party/rust/uniffi_udl/src/resolver.rs new file mode 100644 index 0000000000..14a7a4c6f1 --- /dev/null +++ b/third_party/rust/uniffi_udl/src/resolver.rs @@ -0,0 +1,284 @@ +/* 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 resolving UDL type expressions into concrete types. +//! +//! This module provides the [`TypeResolver`] trait, an abstraction for walking +//! the parse tree of a weedle type expression and using a [`TypeCollector`] to +//! convert it into a concrete type definition (so it assumes that you're already +//! used a [`TypeFinder`](super::TypeFinder) to populate the universe). +//! +//! Perhaps most importantly, it knows how to error out if the UDL tries to reference +//! an undefined or invalid type. + +use anyhow::{bail, Result}; + +use super::{Type, TypeCollector}; + +/// Trait to help resolving an UDL type node to a [`Type`]. +/// +/// This trait does structural matching against type-related weedle AST nodes from +/// a parsed UDL file, turning them into a corresponding [`Type`] struct. It uses the +/// known type definitions in a [`TypeCollector`] to resolve names to types. +/// +/// As a side-effect, resolving a type expression will grow the type universe with +/// references to the types seen during traversal. For example resolving the type +/// expression `sequence<<TestRecord>?` will: +/// +/// * add `Optional<Sequence<TestRecord>` and `Sequence<TestRecord>` to the +/// known types in the universe. +/// * error out if the type name `TestRecord` is not already known. +/// +pub(crate) trait TypeResolver { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type>; +} + +impl TypeResolver for &weedle::types::Type<'_> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + (*self).resolve_type_expression(types) + } +} + +impl TypeResolver for weedle::types::Type<'_> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + match self { + weedle::types::Type::Single(t) => match t { + weedle::types::SingleType::Any(_) => bail!("no support for `any` types"), + weedle::types::SingleType::NonAny(t) => t.resolve_type_expression(types), + }, + weedle::types::Type::Union(_) => bail!("no support for union types yet"), + } + } +} + +impl TypeResolver for weedle::types::NonAnyType<'_> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + match self { + weedle::types::NonAnyType::Boolean(t) => t.resolve_type_expression(types), + weedle::types::NonAnyType::Identifier(t) => t.resolve_type_expression(types), + weedle::types::NonAnyType::Integer(t) => t.resolve_type_expression(types), + weedle::types::NonAnyType::FloatingPoint(t) => t.resolve_type_expression(types), + weedle::types::NonAnyType::Sequence(t) => t.resolve_type_expression(types), + weedle::types::NonAnyType::RecordType(t) => t.resolve_type_expression(types), + _ => bail!("no support for type {:?}", self), + } + } +} + +impl TypeResolver for &weedle::types::AttributedNonAnyType<'_> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + if self.attributes.is_some() { + bail!("type attributes are not supported yet"); + } + self.type_.resolve_type_expression(types) + } +} + +impl TypeResolver for &weedle::types::AttributedType<'_> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + if self.attributes.is_some() { + bail!("type attributes are not supported yet"); + } + self.type_.resolve_type_expression(types) + } +} + +impl<T: TypeResolver> TypeResolver for weedle::types::MayBeNull<T> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + let type_ = self.type_.resolve_type_expression(types)?; + match self.q_mark { + None => Ok(type_), + Some(_) => { + let ty = Type::Optional { + inner_type: Box::new(type_), + }; + Ok(ty) + } + } + } +} + +impl TypeResolver for weedle::types::IntegerType { + fn resolve_type_expression(&self, _types: &mut TypeCollector) -> Result<Type> { + bail!( + "WebIDL integer types not implemented ({:?}); consider using u8, u16, u32 or u64", + self + ) + } +} + +impl TypeResolver for weedle::types::FloatingPointType { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + match self { + weedle::types::FloatingPointType::Float(t) => t.resolve_type_expression(types), + weedle::types::FloatingPointType::Double(t) => t.resolve_type_expression(types), + } + } +} + +impl TypeResolver for weedle::types::SequenceType<'_> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + let t = self.generics.body.as_ref().resolve_type_expression(types)?; + let ty = Type::Sequence { + inner_type: Box::new(t), + }; + Ok(ty) + } +} + +impl TypeResolver for weedle::types::RecordKeyType<'_> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + use weedle::types::RecordKeyType::*; + match self { + Byte(_) | USV(_) => bail!( + "WebIDL Byte or USV string type not implemented ({self:?}); \ + consider using a string", + ), + DOM(_) => Ok(Type::String), + NonAny(t) => t.resolve_type_expression(types), + } + } +} + +impl TypeResolver for weedle::types::RecordType<'_> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + let key_type = self.generics.body.0.resolve_type_expression(types)?; + let value_type = self.generics.body.2.resolve_type_expression(types)?; + let map = Type::Map { + key_type: Box::new(key_type), + value_type: Box::new(value_type), + }; + Ok(map) + } +} + +impl TypeResolver for weedle::common::Identifier<'_> { + fn resolve_type_expression(&self, types: &mut TypeCollector) -> Result<Type> { + match resolve_builtin_type(self.0) { + Some(type_) => Ok(type_), + None => match types.get_type_definition(self.0) { + Some(type_) => Ok(type_), + None => bail!("unknown type reference: {}", self.0), + }, + } + } +} + +impl TypeResolver for weedle::term::Boolean { + fn resolve_type_expression(&self, _types: &mut TypeCollector) -> Result<Type> { + Ok(Type::Boolean) + } +} + +impl TypeResolver for weedle::types::FloatType { + fn resolve_type_expression(&self, _types: &mut TypeCollector) -> Result<Type> { + if self.unrestricted.is_some() { + bail!("we don't support `unrestricted float`"); + } + Ok(Type::Float32) + } +} + +impl TypeResolver for weedle::types::DoubleType { + fn resolve_type_expression(&self, _types: &mut TypeCollector) -> Result<Type> { + if self.unrestricted.is_some() { + bail!("we don't support `unrestricted double`"); + } + Ok(Type::Float64) + } +} + +/// Resolve built-in API types by name. +/// +/// Given an identifier from the UDL, this will return `Some(Type)` if it names one of the +/// built-in primitive types or `None` if it names something else. +pub(crate) fn resolve_builtin_type(name: &str) -> Option<Type> { + match name { + "string" => Some(Type::String), + "bytes" => Some(Type::Bytes), + "u8" => Some(Type::UInt8), + "i8" => Some(Type::Int8), + "u16" => Some(Type::UInt16), + "i16" => Some(Type::Int16), + "u32" => Some(Type::UInt32), + "i32" => Some(Type::Int32), + "u64" => Some(Type::UInt64), + "i64" => Some(Type::Int64), + "f32" => Some(Type::Float32), + "f64" => Some(Type::Float64), + "timestamp" => Some(Type::Timestamp), + "duration" => Some(Type::Duration), + "ForeignExecutor" => Some(Type::ForeignExecutor), + _ => None, + } +} + +#[cfg(test)] +mod test { + use super::*; + use weedle::Parse; + + #[test] + fn test_named_type_resolution() -> Result<()> { + let mut types = TypeCollector::default(); + types.add_type_definition( + "TestRecord", + Type::Record { + name: "TestRecord".into(), + module_path: "".into(), + }, + )?; + assert_eq!(types.type_definitions.len(), 1); + + let (_, expr) = weedle::types::Type::parse("TestRecord").unwrap(); + let t = types.resolve_type_expression(expr).unwrap(); + assert!(matches!(t, Type::Record { name, .. } if name == "TestRecord")); + assert_eq!(types.type_definitions.len(), 1); + + let (_, expr) = weedle::types::Type::parse("TestRecord?").unwrap(); + let t = types.resolve_type_expression(expr).unwrap(); + assert!(matches!(t, Type::Optional { .. })); + assert!(match t { + Type::Optional { inner_type } => + matches!(*inner_type, Type::Record { name, .. } if name == "TestRecord"), + _ => false, + }); + // assert_eq!(types.type_definitions.len(), 2); + + Ok(()) + } + + #[test] + fn test_error_on_unknown_type() -> Result<()> { + let mut types = TypeCollector::default(); + types.add_type_definition( + "TestRecord", + Type::Record { + name: "TestRecord".into(), + module_path: "".into(), + }, + )?; + // Oh no, someone made a typo in the type-o... + let (_, expr) = weedle::types::Type::parse("TestRecrd").unwrap(); + let err = types.resolve_type_expression(expr).unwrap_err(); + assert_eq!(err.to_string(), "unknown type reference: TestRecrd"); + Ok(()) + } + + #[test] + fn test_error_on_union_type() -> Result<()> { + let mut types = TypeCollector::default(); + types.add_type_definition( + "TestRecord", + Type::Record { + name: "TestRecord".into(), + module_path: "".into(), + }, + )?; + let (_, expr) = weedle::types::Type::parse("(TestRecord or u32)").unwrap(); + let err = types.resolve_type_expression(expr).unwrap_err(); + assert_eq!(err.to_string(), "no support for union types yet"); + Ok(()) + } +} |