diff --git a/header-translator/src/lib.rs b/header-translator/src/lib.rs index 7d9c0a512..276e03234 100644 --- a/header-translator/src/lib.rs +++ b/header-translator/src/lib.rs @@ -1,211 +1,16 @@ -use clang::{Entity, EntityKind, EntityVisitResult}; +use clang::Entity; use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use quote::quote; mod method; mod objc2_utils; mod rust_type; -use self::method::Method; +mod stmt; -pub fn get_tokens(entity: &Entity<'_>) -> TokenStream { - match entity.get_kind() { - EntityKind::InclusionDirective - | EntityKind::MacroExpansion - | EntityKind::ObjCClassRef - | EntityKind::ObjCProtocolRef - | EntityKind::MacroDefinition => TokenStream::new(), - EntityKind::ObjCInterfaceDecl => { - // entity.get_mangled_objc_names() - let name = format_ident!("{}", entity.get_name().expect("class name")); - // println!("Availability: {:?}", entity.get_platform_availability()); - let mut superclass_name = None; - let mut protocols = Vec::new(); - let mut methods = Vec::new(); - - entity.visit_children(|entity, _parent| { - match entity.get_kind() { - EntityKind::ObjCIvarDecl => { - // Explicitly ignored - } - EntityKind::ObjCSuperClassRef => { - superclass_name = Some(format_ident!( - "{}", - entity.get_name().expect("superclass name") - )); - } - EntityKind::ObjCRootClass => { - // TODO: Maybe just skip root classes entirely? - superclass_name = Some(format_ident!("Object")); - } - EntityKind::ObjCClassRef => { - println!("ObjCClassRef: {:?}", entity.get_display_name()); - } - EntityKind::ObjCProtocolRef => { - protocols.push(entity); - } - EntityKind::ObjCInstanceMethodDecl | EntityKind::ObjCClassMethodDecl => { - if let Some(method) = Method::parse(entity) { - methods.push(method); - } - } - EntityKind::ObjCPropertyDecl => { - // println!( - // "Property {:?}, {:?}", - // entity.get_display_name().unwrap(), - // entity.get_objc_attributes().unwrap() - // ); - // methods.push(quote! {}); - } - EntityKind::TemplateTypeParameter => { - println!("TODO: Template parameters") - } - EntityKind::VisibilityAttr => { - // NS_CLASS_AVAILABLE_MAC?? - println!("TODO: VisibilityAttr") - } - EntityKind::TypeRef => { - // TODO - } - EntityKind::ObjCException => { - // Maybe useful for knowing when to implement `Error` for the type - } - EntityKind::UnexposedAttr => {} - _ => panic!("Unknown in ObjCInterfaceDecl {:?}", entity), - } - EntityVisitResult::Continue - }); - - let superclass_name = - superclass_name.expect("only classes with a superclass is supported"); - - quote! { - extern_class!( - #[derive(Debug)] - struct #name; - - unsafe impl ClassType for #name { - type Super = #superclass_name; - } - ); - - impl #name { - #(#methods)* - } - } - } - EntityKind::ObjCCategoryDecl => { - let meta = if let Some(doc) = entity.get_name() { - quote!(#[doc = #doc]) - } else { - // Some categories don't have a name. Example: NSClipView - quote!() - }; - let mut class = None; - let mut protocols = Vec::new(); - let mut methods = Vec::new(); - - entity.visit_children(|entity, _parent| { - match entity.get_kind() { - EntityKind::ObjCClassRef => { - if class.is_some() { - panic!("could not find unique category class") - } - class = Some(entity); - } - EntityKind::ObjCProtocolRef => { - protocols.push(entity); - } - EntityKind::ObjCInstanceMethodDecl | EntityKind::ObjCClassMethodDecl => { - if let Some(method) = Method::parse(entity) { - methods.push(method); - } - } - EntityKind::ObjCPropertyDecl => { - // println!( - // "Property {:?}, {:?}", - // entity.get_display_name().unwrap(), - // entity.get_objc_attributes().unwrap() - // ); - // methods.push(quote! {}); - } - EntityKind::TemplateTypeParameter => { - println!("TODO: Template parameters") - } - EntityKind::UnexposedAttr => {} - _ => panic!("Unknown in ObjCCategoryDecl {:?}", entity), - } - EntityVisitResult::Continue - }); - - let class = class.expect("could not find category class"); - let class_name = format_ident!("{}", class.get_name().expect("class name")); - - quote! { - #meta - impl #class_name { - #(#methods)* - } - } - } - EntityKind::ObjCProtocolDecl => { - let name = format_ident!("{}", entity.get_name().expect("protocol name")); - let mut protocols = Vec::new(); - let mut methods = Vec::new(); - - entity.visit_children(|entity, _parent| { - match entity.get_kind() { - EntityKind::ObjCExplicitProtocolImpl => { - // TODO - } - EntityKind::ObjCProtocolRef => { - protocols.push(entity); - } - EntityKind::ObjCInstanceMethodDecl | EntityKind::ObjCClassMethodDecl => { - // TODO: Required vs. optional methods - if let Some(method) = Method::parse(entity) { - methods.push(method); - } - } - EntityKind::ObjCPropertyDecl => { - // TODO - } - EntityKind::UnexposedAttr => {} - _ => panic!("Unknown in ObjCProtocolDecl {:?}", entity), - } - EntityVisitResult::Continue - }); - - quote! { - extern_protocol!( - #[derive(Debug)] - struct #name; - - unsafe impl ProtocolType for #name { - type Super = todo!(); - } - ); - - impl #name { - #(#methods)* - } - } - } - EntityKind::EnumDecl - | EntityKind::VarDecl - | EntityKind::FunctionDecl - | EntityKind::TypedefDecl - | EntityKind::StructDecl => { - // TODO - TokenStream::new() - } - _ => { - panic!("Unknown: {:?}", entity) - } - } -} +use self::stmt::Stmt; pub fn create_rust_file(entities: &[Entity<'_>]) -> TokenStream { - let iter = entities.iter().map(get_tokens); + let iter = entities.iter().filter_map(Stmt::parse); quote! { #(#iter)* } diff --git a/header-translator/src/main.rs b/header-translator/src/main.rs index 56a9fd432..233c70ea7 100644 --- a/header-translator/src/main.rs +++ b/header-translator/src/main.rs @@ -144,10 +144,7 @@ fn main() { drop(stdin); let output = child.wait_with_output().expect("failed formatting"); - // println!( - // "{}", - // String::from_utf8(output.stdout).unwrap() - // ); + // println!("{}", String::from_utf8(output.stdout).unwrap()); } // } diff --git a/header-translator/src/stmt.rs b/header-translator/src/stmt.rs new file mode 100644 index 000000000..7798ad096 --- /dev/null +++ b/header-translator/src/stmt.rs @@ -0,0 +1,277 @@ +use clang::{Entity, EntityKind, EntityVisitResult}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens, TokenStreamExt}; + +use crate::method::Method; + +#[derive(Debug, Clone)] +pub enum Stmt { + /// @interface + ClassDecl { + name: String, + // TODO: Generics + superclass: Option, + protocols: Vec, + methods: Vec, + }, + CategoryDecl { + class_name: String, + /// Some categories don't have a name. Example: NSClipView + name: Option, + /// I don't quite know what this means? + protocols: Vec, + methods: Vec, + }, + ProtocolDecl { + name: String, + protocols: Vec, + methods: Vec, + }, +} + +impl Stmt { + pub fn parse(entity: &Entity<'_>) -> Option { + match entity.get_kind() { + EntityKind::InclusionDirective + | EntityKind::MacroExpansion + | EntityKind::ObjCClassRef + | EntityKind::ObjCProtocolRef + | EntityKind::MacroDefinition => None, + EntityKind::ObjCInterfaceDecl => { + // entity.get_mangled_objc_names() + let name = entity.get_name().expect("class name"); + // println!("Availability: {:?}", entity.get_platform_availability()); + let mut superclass = None; + let mut protocols = Vec::new(); + let mut methods = Vec::new(); + + entity.visit_children(|entity, _parent| { + match entity.get_kind() { + EntityKind::ObjCIvarDecl => { + // Explicitly ignored + } + EntityKind::ObjCSuperClassRef => { + superclass = Some(Some(entity.get_name().expect("superclass name"))); + } + EntityKind::ObjCRootClass => { + // TODO: Maybe just skip root classes entirely? + superclass = Some(None); + } + EntityKind::ObjCClassRef => { + println!("ObjCClassRef: {:?}", entity.get_display_name()); + } + EntityKind::ObjCProtocolRef => { + protocols.push(entity.get_name().expect("protocolref to have name")); + } + EntityKind::ObjCInstanceMethodDecl | EntityKind::ObjCClassMethodDecl => { + if let Some(method) = Method::parse(entity) { + methods.push(method); + } + } + EntityKind::ObjCPropertyDecl => { + // println!( + // "Property {:?}, {:?}", + // entity.get_display_name().unwrap(), + // entity.get_objc_attributes().unwrap() + // ); + // methods.push(quote! {}); + } + EntityKind::TemplateTypeParameter => { + println!("TODO: Template parameters") + } + EntityKind::VisibilityAttr => { + // NS_CLASS_AVAILABLE_MAC?? + println!("TODO: VisibilityAttr") + } + EntityKind::TypeRef => { + // TODO + } + EntityKind::ObjCException => { + // Maybe useful for knowing when to implement `Error` for the type + } + EntityKind::UnexposedAttr => {} + _ => panic!("Unknown in ObjCInterfaceDecl {:?}", entity), + } + EntityVisitResult::Continue + }); + + let superclass = superclass.expect("no superclass found"); + + Some(Self::ClassDecl { + name, + superclass, + protocols, + methods, + }) + } + EntityKind::ObjCCategoryDecl => { + let mut class = None; + let mut protocols = Vec::new(); + let mut methods = Vec::new(); + + entity.visit_children(|entity, _parent| { + match entity.get_kind() { + EntityKind::ObjCClassRef => { + if class.is_some() { + panic!("could not find unique category class") + } + class = Some(entity); + } + EntityKind::ObjCProtocolRef => { + protocols.push(entity.get_name().expect("protocolref to have name")); + } + EntityKind::ObjCInstanceMethodDecl | EntityKind::ObjCClassMethodDecl => { + if let Some(method) = Method::parse(entity) { + methods.push(method); + } + } + EntityKind::ObjCPropertyDecl => { + // println!( + // "Property {:?}, {:?}", + // entity.get_display_name().unwrap(), + // entity.get_objc_attributes().unwrap() + // ); + // methods.push(quote! {}); + } + EntityKind::TemplateTypeParameter => { + println!("TODO: Template parameters") + } + EntityKind::UnexposedAttr => {} + _ => panic!("Unknown in ObjCCategoryDecl {:?}", entity), + } + EntityVisitResult::Continue + }); + + let class = class.expect("could not find category class"); + let class_name = class.get_name().expect("class name"); + + Some(Self::CategoryDecl { + class_name, + name: entity.get_name(), + protocols, + methods, + }) + } + EntityKind::ObjCProtocolDecl => { + let name = entity.get_name().expect("protocol name"); + let mut protocols = Vec::new(); + let mut methods = Vec::new(); + + entity.visit_children(|entity, _parent| { + match entity.get_kind() { + EntityKind::ObjCExplicitProtocolImpl => { + // TODO + } + EntityKind::ObjCProtocolRef => { + protocols.push(entity.get_name().expect("protocolref to have name")); + } + EntityKind::ObjCInstanceMethodDecl | EntityKind::ObjCClassMethodDecl => { + // TODO: Required vs. optional methods + if let Some(method) = Method::parse(entity) { + methods.push(method); + } + } + EntityKind::ObjCPropertyDecl => { + // TODO + } + EntityKind::UnexposedAttr => {} + _ => panic!("Unknown in ObjCProtocolDecl {:?}", entity), + } + EntityVisitResult::Continue + }); + + Some(Self::ProtocolDecl { + name, + protocols, + methods, + }) + } + EntityKind::EnumDecl + | EntityKind::VarDecl + | EntityKind::FunctionDecl + | EntityKind::TypedefDecl + | EntityKind::StructDecl => { + // TODO + None + } + _ => { + panic!("Unknown: {:?}", entity) + } + } + } +} + +impl ToTokens for Stmt { + fn to_tokens(&self, tokens: &mut TokenStream) { + let result = match self { + Self::ClassDecl { + name, + superclass, + protocols, + methods, + } => { + let name = format_ident!("{}", name); + let superclass_name = + format_ident!("{}", superclass.as_deref().unwrap_or("Object")); + + quote! { + extern_class!( + #[derive(Debug)] + struct #name; + + unsafe impl ClassType for #name { + type Super = #superclass_name; + } + ); + + impl #name { + #(#methods)* + } + } + } + Self::CategoryDecl { + class_name, + name, + protocols, + methods, + } => { + let meta = if let Some(name) = name { + quote!(#[doc = #name]) + } else { + quote!() + }; + let class_name = format_ident!("{}", class_name); + + quote! { + #meta + impl #class_name { + #(#methods)* + } + } + } + Self::ProtocolDecl { + name, + protocols, + methods, + } => { + let name = format_ident!("{}", name); + + quote! { + extern_protocol!( + #[derive(Debug)] + struct #name; + + unsafe impl ProtocolType for #name { + type Super = todo!(); + } + ); + + impl #name { + #(#methods)* + } + } + } + }; + tokens.append_all(result); + } +}