use anyhow::{Context, Result};
use std::{collections::hash_map::Entry, collections::BTreeSet, collections::HashMap};
pub use uniffi_meta::{AsType, NamespaceMetadata, ObjectImpl, Type, TypeIterator};
#[derive(Clone, Debug, Default)]
pub(crate) struct TypeUniverse {
pub namespace: NamespaceMetadata,
pub namespace_docstring: Option<String>,
pub(super) type_definitions: HashMap<String, Type>,
pub(super) all_known_types: BTreeSet<Type>,
}
impl TypeUniverse {
pub fn new(namespace: NamespaceMetadata) -> Self {
Self {
namespace,
..Default::default()
}
}
fn add_type_definition(&mut self, name: &str, type_: &Type) -> Result<()> {
match self.type_definitions.entry(name.to_string()) {
Entry::Occupied(o) => {
let cur = o.get();
anyhow::ensure!(
normalize_type_module_path(type_) == normalize_type_module_path(cur),
"conflicting types:\ncur: {cur:?}\nnew: {type_:?}",
);
Ok(())
}
Entry::Vacant(e) => {
e.insert(type_.clone());
Ok(())
}
}
}
pub(super) fn get_type_definition(&self, name: &str) -> Option<Type> {
self.type_definitions.get(name).cloned()
}
pub fn add_known_type(&mut self, type_: &Type) -> Result<()> {
if !self.all_known_types.contains(type_) {
self.all_known_types
.insert(normalize_type_module_path(type_));
}
for sub in type_.iter_nested_types() {
self.add_known_type(sub)?;
}
if let Some(name) = type_.name() {
self.add_type_definition(name, type_)
.with_context(|| format!("adding named type {name}"))?;
}
Ok(())
}
pub fn add_known_types(&mut self, types: TypeIterator<'_>) -> Result<()> {
for t in types {
self.add_known_type(t)
.with_context(|| format!("adding type {t:?}"))?
}
Ok(())
}
pub fn is_external(&self, t: &Type) -> bool {
t.crate_name()
.map(|p| p != self.namespace.crate_name)
.unwrap_or(false)
}
#[cfg(test)]
pub fn contains(&self, type_: &Type) -> bool {
self.all_known_types.contains(type_)
}
pub fn iter_local_types(&self) -> impl Iterator<Item = &Type> {
self.filter_local_types(self.all_known_types.iter())
}
pub fn iter_external_types(&self) -> impl Iterator<Item = &Type> {
self.all_known_types.iter().filter(|t| self.is_external(t))
}
pub fn filter_local_types<'a>(
&'a self,
types: impl Iterator<Item = &'a Type>,
) -> impl Iterator<Item = &'a Type> {
types.filter(|t| !self.is_external(t))
}
}
fn normalize_type_module_path(ty: &Type) -> Type {
match ty {
Type::UInt8
| Type::Int8
| Type::UInt16
| Type::Int16
| Type::UInt32
| Type::Int32
| Type::UInt64
| Type::Int64
| Type::Float32
| Type::Float64
| Type::Boolean
| Type::String
| Type::Bytes
| Type::Timestamp
| Type::Duration => ty.clone(),
Type::Object {
name,
imp,
module_path,
} => Type::Object {
module_path: normalize_module_path(module_path),
name: name.clone(),
imp: *imp,
},
Type::Record { name, module_path } => Type::Record {
module_path: normalize_module_path(module_path),
name: name.clone(),
},
Type::Enum { name, module_path } => Type::Enum {
module_path: normalize_module_path(module_path),
name: name.clone(),
},
Type::CallbackInterface { name, module_path } => Type::CallbackInterface {
module_path: normalize_module_path(module_path),
name: name.clone(),
},
Type::Optional { inner_type } => Type::Optional {
inner_type: Box::new(normalize_type_module_path(inner_type)),
},
Type::Sequence { inner_type } => Type::Sequence {
inner_type: Box::new(normalize_type_module_path(inner_type)),
},
Type::Map {
key_type,
value_type,
} => Type::Map {
key_type: Box::new(normalize_type_module_path(key_type)),
value_type: Box::new(normalize_type_module_path(value_type)),
},
Type::Custom {
name,
builtin,
module_path,
} => Type::Custom {
module_path: normalize_module_path(module_path),
name: name.clone(),
builtin: Box::new(normalize_type_module_path(builtin)),
},
}
}
fn normalize_module_path(module_path: &str) -> String {
module_path.split("::").next().unwrap().to_string()
}
#[cfg(test)]
mod test_type_universe {
}