use super::*;
#[derive(Debug)]
pub struct InterfaceMethod {
pub name: String,
pub params: Vec<Param>,
pub return_type: metadata::Type,
pub is_propget: bool,
pub is_propput: bool,
}
#[derive(Debug)]
pub struct Interface {
pub name: String,
pub guid: Option<String>,
pub base: Option<metadata::Type>,
pub methods: Vec<InterfaceMethod>,
}
impl Interface {
pub fn parse(cursor: Cursor, parser: &mut Parser<'_>) -> Result<Self, Error> {
let tag_name = cursor.name();
let name = parser
.tag_rename
.get(&tag_name)
.cloned()
.unwrap_or(tag_name);
let guid = cursor.extract_uuid(parser.tu);
let base = cursor.children().iter().find_map(|c| {
if c.kind() == CXCursor_CXXBaseSpecifier {
let base_name = c.ty().ty().name();
if !base_name.is_empty() {
let base_ns = if parser.header_root.is_some() {
parser.namespace.to_string()
} else {
parser
.ref_map
.get(&base_name)
.map_or(parser.namespace, |s| s.as_str())
.to_string()
};
return Some(metadata::Type::value_named(&base_ns, &base_name));
}
}
None
});
let mut methods = vec![];
for child in cursor.children() {
if child.kind() != CXCursor_CXXMethod || !child.is_pure_virtual() {
continue;
}
if child.overrides_base_method() {
continue;
}
let method_name = demacro_member_name(child.name(), parser.macro_defs);
let tokens = parser
.tu
.tokenize(parser.tu.to_expansion_range(child.extent()));
let method_annotation = extract_method_annotation(&tokens, &method_name);
let midl_param_annotations =
scan_method_param_annotations(&tokens, &method_name, parser.macro_defs);
let return_type = child.result_type().to_type(parser);
let mut params = parse_params(&child, &midl_param_annotations, parser);
infer_iid_is(&mut params, &return_type);
methods.push(InterfaceMethod {
name: method_name,
params,
return_type,
is_propget: method_annotation.is_propget,
is_propput: method_annotation.is_propput,
});
}
let mut start = 0;
while start < methods.len() {
let mut end = start + 1;
while end < methods.len() && methods[end].name == methods[start].name {
end += 1;
}
methods[start..end].reverse();
start = end;
}
Ok(Self {
name,
guid,
base,
methods,
})
}
pub fn write(&self, namespace: &str) -> Result<TokenStream, Error> {
let name = write_ident(&self.name);
let guid_token = if let Some(uuid) = &self.guid {
let lit_str = uuid_to_u128_literal(uuid);
let lit = syn::LitInt::new(&lit_str, Span::call_site());
quote! { #[guid(#lit)] }
} else {
quote! { #[no_guid] }
};
let requires_token = if let Some(base_type) = &self.base {
let base_tokens = write_type(namespace, base_type);
quote! { : #base_tokens }
} else {
quote! {}
};
let methods_tokens: Vec<TokenStream> = self
.methods
.iter()
.map(|m| {
let mname = write_ident(&m.name);
let params = m.params.iter().map(|p| {
let pname = write_ident(&p.name);
let pty = write_type(namespace, &p.ty);
let attrs = param_attrs_for_annotation(&p.annotation, &p.ty);
quote! { #(#attrs)* #pname: #pty }
});
let return_type = match &m.return_type {
metadata::Type::Void => quote! {},
ty => {
let ty = write_type(namespace, ty);
quote! { -> #ty }
}
};
let special_attr = if m.is_propget || m.is_propput {
quote! { #[special] }
} else {
quote! {}
};
quote! { #special_attr fn #mname(&self, #(#params),*) #return_type; }
})
.collect();
Ok(quote! {
#guid_token
interface #name #requires_token {
#(#methods_tokens)*
}
})
}
}