use crate::attributes;
use crate::converters::{convert_docstring, APIConverter};
use crate::finder;
use crate::resolver::TypeResolver;
use anyhow::{bail, Result};
use std::collections::{hash_map, BTreeSet, HashMap};
use uniffi_meta::Type;
#[derive(Debug, Default)]
pub(crate) struct InterfaceCollector {
pub types: TypeCollector,
pub items: BTreeSet<uniffi_meta::Metadata>,
}
impl InterfaceCollector {
pub fn from_webidl(idl: &str, crate_name: &str) -> Result<Self> {
let mut ci = Self::default();
use weedle::Parse; let (remaining, defns) = weedle::Definitions::parse(idl.trim()).unwrap();
if !remaining.is_empty() {
println!("Error parsing the IDL. Text remaining to be parsed is:");
println!("{remaining}");
bail!("parse error");
}
ci.types.namespace = ci.find_namespace(&defns)?;
ci.types.crate_name = crate_name.to_string();
ci.types.add_type_definitions_from(defns.as_slice())?;
APIBuilder::process(&defns, &mut ci)?;
for t in ci.types.type_definitions.values() {
if let Type::Custom {
module_path,
name,
builtin,
} = t
{
ci.items.insert(
uniffi_meta::CustomTypeMetadata {
module_path: module_path.clone(),
name: name.clone(),
builtin: *builtin.clone(),
docstring: None,
}
.into(),
);
}
}
Ok(ci)
}
fn find_namespace(&mut self, defns: &Vec<weedle::Definition<'_>>) -> Result<String> {
for defn in defns {
if let weedle::Definition::Namespace(n) = defn {
return Ok(n.identifier.0.to_string());
}
}
bail!("Failed to find the namespace");
}
pub fn module_path(&self) -> String {
self.types.module_path()
}
pub fn get_type(&self, name: &str) -> Option<Type> {
self.types.get_type_definition(name)
}
pub fn resolve_type_expression<T: TypeResolver>(&mut self, expr: T) -> Result<Type> {
self.types.resolve_type_expression(expr)
}
pub fn resolve_return_type_expression(
&mut self,
expr: &weedle::types::ReturnType<'_>,
) -> Result<Option<Type>> {
Ok(match expr {
weedle::types::ReturnType::Undefined(_) => None,
weedle::types::ReturnType::Type(t) => {
use weedle::types::{NonAnyType::Identifier, SingleType::NonAny, Type::Single};
match t {
Single(NonAny(Identifier(id))) if id.type_.0 == "void" => None,
_ => Some(self.resolve_type_expression(t)?),
}
}
})
}
fn add_definition(&mut self, defn: uniffi_meta::Metadata) -> Result<()> {
self.items.insert(defn);
Ok(())
}
}
impl From<InterfaceCollector> for uniffi_meta::MetadataGroup {
fn from(value: InterfaceCollector) -> Self {
Self {
namespace: uniffi_meta::NamespaceMetadata {
crate_name: value.types.module_path(),
name: value.types.namespace,
},
namespace_docstring: value.types.namespace_docstring.clone(),
items: value.items,
}
}
}
trait APIBuilder {
fn process(&self, ci: &mut InterfaceCollector) -> Result<()>;
}
impl<T: APIBuilder> APIBuilder for Vec<T> {
fn process(&self, ci: &mut InterfaceCollector) -> Result<()> {
for item in self {
item.process(ci)?;
}
Ok(())
}
}
impl APIBuilder for weedle::Definition<'_> {
fn process(&self, ci: &mut InterfaceCollector) -> Result<()> {
match self {
weedle::Definition::Namespace(d) => d.process(ci)?,
weedle::Definition::Enum(d) => {
let e: uniffi_meta::EnumMetadata = d.convert(ci)?;
ci.add_definition(e.into())?;
}
weedle::Definition::Dictionary(d) => {
let rec = d.convert(ci)?;
ci.add_definition(rec.into())?;
}
weedle::Definition::Interface(d) => {
let attrs = attributes::InterfaceAttributes::try_from(d.attributes.as_ref())?;
if attrs.contains_enum_attr() || attrs.contains_error_attr() {
let e: uniffi_meta::EnumMetadata = d.convert(ci)?;
ci.add_definition(e.into())?;
} else {
let obj: uniffi_meta::ObjectMetadata = d.convert(ci)?;
ci.add_definition(obj.into())?;
}
}
weedle::Definition::CallbackInterface(d) => {
let obj = d.convert(ci)?;
ci.add_definition(obj.into())?;
}
weedle::Definition::Typedef(_) => {}
_ => bail!("don't know how to deal with {:?}", self),
}
Ok(())
}
}
impl APIBuilder for weedle::NamespaceDefinition<'_> {
fn process(&self, ci: &mut InterfaceCollector) -> Result<()> {
if self.attributes.is_some() {
bail!("namespace attributes are not supported yet");
}
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())?;
}
Ok(())
}
}
#[derive(Debug, Default)]
pub(crate) struct TypeCollector {
pub namespace: String,
pub namespace_docstring: Option<String>,
pub crate_name: String,
pub type_definitions: HashMap<String, Type>,
}
impl TypeCollector {
pub fn module_path(&self) -> String {
self.crate_name.clone()
}
pub fn add_type_definitions_from<T: finder::TypeFinder>(&mut self, defn: T) -> Result<()> {
defn.add_type_definitions_to(self)
}
pub fn add_type_definition(&mut self, name: &str, type_: Type) -> Result<()> {
match self.type_definitions.entry(name.to_string()) {
hash_map::Entry::Occupied(o) => {
let existing_def = o.get();
if type_ == *existing_def
&& matches!(type_, Type::Record { .. } | Type::Enum { .. })
{
Ok(())
} else {
bail!(
"Conflicting type definition for `{name}`! \
existing definition: {existing_def:?}, \
new definition: {type_:?}"
);
}
}
hash_map::Entry::Vacant(e) => {
e.insert(type_);
Ok(())
}
}
}
pub fn get_type_definition(&self, name: &str) -> Option<Type> {
self.type_definitions.get(name).cloned()
}
pub fn resolve_type_expression<T: TypeResolver>(&mut self, expr: T) -> Result<Type> {
expr.resolve_type_expression(self)
}
}