Revision control
Copy as Markdown
Other Tools
/* 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
//! # 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),
_ => 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(())
}
}