/// Custom Diplomat attribute that can be placed on a struct definition. #[derive(Debug)] enum DiplomatStructAttribute { /// The `#[diplomat::out]` attribute, used for non-opaque structs that /// contain an owned opaque in the form of a `Box`.
Out, /// An attribute that can correspond to a type (struct or enum).
TypeAttr(DiplomatTypeAttribute),
}
/// Custom Diplomat attribute that can be placed on an enum or struct definition. #[derive(Debug)] enum DiplomatTypeAttribute { /// The `#[diplomat::opaque]` attribute, used for marking a type as opaque. /// Note that opaque structs can be borrowed in return types, but cannot /// be passed into a function behind a mutable reference.
Opaque, /// The `#[diplomat::opaque_mut]` attribute, used for marking a type as /// opaque and mutable. /// Note that mutable opaque types can never be borrowed in return types /// (even immutably!), but can be passed into a function behind a mutable /// reference.
OpaqueMut,
}
impl DiplomatStructAttribute { /// Parses a [`DiplomatStructAttribute`] from an array of [`syn::Attribute`]s. /// If more than one kind is found, an error is returned containing all the /// ones encountered, since all the current attributes are disjoint. fn parse(attrs: &[syn::Attribute]) -> Result<Option<Self>, Vec<Self>> { letmut buf = String::with_capacity(32); letmut res = Ok(None); for attr in attrs {
buf.clear();
write!(&mut buf, "{}", attr.path().to_token_stream()).unwrap(); let parsed = match buf.as_str() { "diplomat :: out" => Some(Self::Out), "diplomat :: opaque" => Some(Self::TypeAttr(DiplomatTypeAttribute::Opaque)), "diplomat :: opaque_mut" => Some(Self::TypeAttr(DiplomatTypeAttribute::OpaqueMut)),
_ => None,
};
iflet Some(parsed) = parsed { match res {
Ok(None) => res = Ok(Some(parsed)),
Ok(Some(first)) => res = Err(vec![first, parsed]),
Err(refmut errors) => errors.push(parsed),
}
}
}
res
}
}
impl DiplomatTypeAttribute { /// Parses a [`DiplomatTypeAttribute`] from an array of [`syn::Attribute`]s. /// If more than one kind is found, an error is returned containing all the /// ones encountered, since all the current attributes are disjoint. fn parse(attrs: &[syn::Attribute]) -> Result<Option<Self>, Vec<Self>> { letmut buf = String::with_capacity(32); letmut res = Ok(None); for attr in attrs {
buf.clear();
write!(&mut buf, "{}", attr.path().to_token_stream()).unwrap(); let parsed = match buf.as_str() { "diplomat :: opaque" => Some(Self::Opaque), "diplomat :: opaque_mut" => Some(Self::OpaqueMut),
_ => None,
};
iflet Some(parsed) = parsed { match res {
Ok(None) => res = Ok(Some(parsed)),
Ok(Some(first)) => res = Err(vec![first, parsed]),
Err(refmut errors) => errors.push(parsed),
}
}
}
/// Contains all items needed to build an AST representation of a given [`Module`], /// as we traverse through [`syn::ItemMod`]. We build this up in [`ModuleBuilder::add`] struct ModuleBuilder {
custom_types_by_name: BTreeMap<Ident, CustomType>,
custom_traits_by_name: BTreeMap<Ident, Trait>,
functions_by_name: BTreeMap<Ident, Function>,
sub_modules: Vec<Module>,
imports: Vec<(Path, Ident)>, /// As we traverse through the module, are we inside of #[diplomat::bridge]? /// If so, then `analyze_types` is set to true, and types, functions, and traits are all updated according to information parsed. /// /// Otherwise, we traverse through modules until we find a module marked by #[diplomat::bridge]
analyze_types: bool,
type_parent_attrs: Attrs,
impl_parent_attrs: Attrs,
mod_macros: Macros,
}
impl ModuleBuilder { fn add(&mutself, a: &Item) { match a {
Item::Use(u) => { ifself.analyze_types {
extract_imports(&Path::empty(), &u.tree, &mutself.imports);
}
}
Item::Struct(strct) => { ifself.analyze_types { let custom_type = match DiplomatStructAttribute::parse(&strct.attrs[..]) {
Ok(None) => {
CustomType::Struct(Struct::new(strct, false, &self.type_parent_attrs))
}
Ok(Some(DiplomatStructAttribute::Out)) => {
CustomType::Struct(Struct::new(strct, true, &self.type_parent_attrs))
}
Ok(Some(DiplomatStructAttribute::TypeAttr(
DiplomatTypeAttribute::Opaque,
))) => CustomType::Opaque(OpaqueType::new_struct(
strct,
Mutability::Immutable,
&self.type_parent_attrs,
)),
Ok(Some(DiplomatStructAttribute::TypeAttr(
DiplomatTypeAttribute::OpaqueMut,
))) => CustomType::Opaque(OpaqueType::new_struct(
strct,
Mutability::Mutable,
&self.type_parent_attrs,
)),
Err(errors) => {
panic!("Multiple conflicting Diplomat struct attributes, there can be at most one: {errors:?}");
}
};
Item::Enum(enm) => { ifself.analyze_types { let ident = (&enm.ident).into(); let custom_enum = match DiplomatTypeAttribute::parse(&enm.attrs[..]) {
Ok(None) => CustomType::Enum(Enum::new(enm, &self.type_parent_attrs)),
Ok(Some(DiplomatTypeAttribute::Opaque)) => {
CustomType::Opaque(OpaqueType::new_enum(
enm,
Mutability::Immutable,
&self.type_parent_attrs,
))
}
Ok(Some(DiplomatTypeAttribute::OpaqueMut)) => CustomType::Opaque(
OpaqueType::new_enum(enm, Mutability::Mutable, &self.type_parent_attrs),
),
Err(errors) => {
panic!("Multiple conflicting Diplomat enum attributes, there can be at most one: {errors:?}");
}
}; self.custom_types_by_name.insert(ident, custom_enum);
}
}
Item::Impl(imp) => { ifself.analyze_types && imp.trait_.is_none() { let self_path = match imp.self_ty.as_ref() {
syn::Type::Path(s) => PathType::from(s),
_ => panic!("Self type not found"),
}; letmut impl_attrs = self.impl_parent_attrs.clone();
impl_attrs.add_attrs(&imp.attrs); let method_parent_attrs =
impl_attrs.attrs_for_inheritance(AttrInheritContext::MethodFromImpl); let self_ident = self_path.path.elements.last().unwrap();
// Do a prepass to evaluate macros: letmut impl_item_vec = Vec::new(); for i in &imp.items { match i {
ImplItem::Fn(f) => {
impl_item_vec.push(ImplItem::Fn(f.clone()));
}
ImplItem::Macro(mac) => { letmut items = self.mod_macros.evaluate_impl_item_macro(mac);
impl_item_vec.append(&mut items);
}
_ => {}
}
}
// Then only add functions to the block: letmut new_methods = impl_item_vec
.iter()
.filter_map(|i| match i {
ImplItem::Fn(m) => Some(m),
_ => None,
})
.filter(|m| { let is_public = matches!(m.vis, Visibility::Public(_)); let has_diplomat_attrs = m.attrs.iter().any(|a| {
a.path().segments.iter().next().unwrap().ident == "diplomat"
});
assert!(
is_public || !has_diplomat_attrs, "Non-public method with diplomat attrs found: {self_ident}::{}",
m.sig.ident
);
is_public
})
.map(|m| {
Method::from_syn(
m,
self_path.clone(),
Some(&imp.generics),
&method_parent_attrs,
)
})
.collect();
matchself.custom_types_by_name.get_mut(self_ident)
.expect("Diplomat currently requires impls to be in the same module as their self type") {
CustomType::Struct(strct) => {
strct.methods.append(&mut new_methods);
}
CustomType::Opaque(strct) => {
strct.methods.append(&mut new_methods);
}
CustomType::Enum(enm) => {
enm.methods.append(&mut new_methods);
}
}
}
}
Item::Mod(item_mod) => { self.sub_modules.push(Module::from_syn(item_mod, false));
}
Item::Trait(trt) => { ifself.analyze_types { let ident = (&trt.ident).into(); let trt = Trait::new(trt, &self.type_parent_attrs); self.custom_traits_by_name.insert(ident, trt);
}
}
Item::Macro(mac) => { ifself.analyze_types { iflet Some(i) = &mac.ident { let macro_rules_attr = mac.attrs.iter().find(|a| {
a.path()
== &syn::parse_str::<syn::Path>("diplomat::macro_rules").unwrap()
});
if macro_rules_attr.is_some() { self.mod_macros.add_item_macro(mac);
} else {
println!(
r#"WARNING: Found macro_rules definition "macro_rules! {i}" with no #[diplomat::macro_rules] attribute. This will not be evaluated in Diplomat bindings."#
);
}
} else { let items = self.mod_macros.evaluate_item_macro(mac); for i in items { self.add(&i);
}
}
}
}
Item::Fn(f) => { ifself.analyze_types { let is_public = matches!(f.vis, Visibility::Public(_)); let has_diplomat_attrs = f
.attrs
.iter()
.any(|a| a.path().segments.iter().next().unwrap().ident == "diplomat");
assert!(
is_public || !has_diplomat_attrs, "Non-public function with diplomat attrs found: {}",
f.sig.ident
); if is_public { let parent_attrs = self
.impl_parent_attrs
.attrs_for_inheritance(AttrInheritContext::MethodFromImpl); let out = Function::from_syn(f, &parent_attrs); self.functions_by_name.insert(out.name.clone(), out);
}
}
}
_ => {}
}
}
}
self.declared_types.iter().for_each(|(k, v)| { if mod_symbols
.insert(k.clone(), ModSymbol::CustomType(v.clone()))
.is_some()
{
panic!("Two types were declared with the same name, this needs to be implemented (key: {k})");
}
});
self.declared_traits.iter().for_each(|(k, v)| { if mod_symbols
.insert(k.clone(), ModSymbol::Trait(v.clone()))
.is_some()
{
panic!("Two traits were declared with the same name, this needs to be implemented (key: {k})");
}
});
self.declared_functions.iter().for_each(|(k, f)| { if mod_symbols.insert(k.clone(), ModSymbol::Function(f.clone())).is_some() {
panic!("Two functions were declared with the same name, this needs to be implemented (key: {k})")
}
});
/// Convert an [`ItemMod`] to a [`Module`]. /// /// `force_analyze` is for forcibly parsing the module in the case where we know the `#[diplomat::bridge]` attribute should be present, /// but proc_macro (or some other analyzer) has removed the attribute in advance. pubfn from_syn(input: &ItemMod, force_analyze: bool) -> Module { let mod_attrs: Attrs = (&*input.attrs).into();
impl From<&syn::File> for File { /// Get all custom types across all modules defined in a given file. fn from(file: &syn::File) -> File { letmut out = BTreeMap::new();
file.items.iter().for_each(|i| { iflet Item::Mod(item_mod) = i {
out.insert(
item_mod.ident.to_string(),
Module::from_syn(item_mod, false),
);
}
});
File { modules: out }
}
}
#[cfg(test)] mod tests { use insta::{self, Settings};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.