fix(macros): Generate namespacesed types and convertible impls

This commit is contained in:
Benedikt Peetz 2024-03-24 21:06:14 +01:00
parent fa26bdfc6c
commit 5aaa2b43f3
Signed by: bpeetz
GPG Key ID: A5E94010C3A642AD
2 changed files with 189 additions and 62 deletions

View File

@ -29,16 +29,23 @@ use convert_case::{Case, Casing};
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use trixy_parser::command_spec::{ use trixy_parser::command_spec::{
Attribute, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier, CommandSpec, Enumeration, Function, Identifier, Namespace, Structure,
Namespace, Structure,
}; };
use crate::{ use crate::{
config::TrixyConfig, config::TrixyConfig,
generate::{identifier_to_rust, named_type_to_rust}, generate::{
attribute_to_rust,
c_api::{mangle_c_type_identifier, type_variant_c_path},
convertible_derive::{
rust_enumeration_into_impl, structure_convertable_derive, structure_into_impl,
},
doc_identifier_to_rust, doc_named_type_to_rust, identifier_to_rust, named_type_to_rust,
type_variant_rust_path,
},
}; };
use super::{c_api::type_to_c_equalivalent, function_identifier_to_rust}; use super::{c_api::type_to_c_equivalent, function_identifier_to_rust};
thread_local! {static DEBUG: OnceCell<TokenStream2> = OnceCell::new();} thread_local! {static DEBUG: OnceCell<TokenStream2> = OnceCell::new();}
@ -66,13 +73,13 @@ thread_local! {static DEBUG: OnceCell<TokenStream2> = OnceCell::new();}
/// } /// }
/// pub mod trinitrix { /// pub mod trinitrix {
/// #[allow(non_camel_case_types)] /// #[allow(non_camel_case_types)]
/// #[derive(Debug, Convertible)] /// #[derive(Debug)]
/// struct Callback { /// struct Callback {
/// func: String, /// func: String,
/// timeout: String, /// timeout: String,
/// } /// }
/// #[allow(non_camel_case_types)] /// #[allow(non_camel_case_types)]
/// #[derive(Debug, Convertible)] /// #[derive(Debug)]
/// enum CallbackPriority { /// enum CallbackPriority {
/// High, /// High,
/// Medium, /// Medium,
@ -122,9 +129,6 @@ pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 {
let debug = get_debug_sate(); let debug = get_debug_sate();
quote! { quote! {
#[allow(unused_imports)]
use trixy::types::traits::convert_trait::*;
#structures #structures
#enumerations #enumerations
#debug #debug
@ -145,7 +149,7 @@ fn namespace_to_module(namespace: &Namespace, namespaces: &Vec<&Identifier>) ->
let doc_comments: TokenStream2 = namespace let doc_comments: TokenStream2 = namespace
.attributes .attributes
.iter() .iter()
.map(attribute_to_doc_comment) .map(|attr| attribute_to_rust(&namespace.name, attr))
.collect(); .collect();
let structures: TokenStream2 = namespace.structures.iter().map(structure_to_rust).collect(); let structures: TokenStream2 = namespace.structures.iter().map(structure_to_rust).collect();
let enumerations: TokenStream2 = namespace let enumerations: TokenStream2 = namespace
@ -173,9 +177,6 @@ fn namespace_to_module(namespace: &Namespace, namespaces: &Vec<&Identifier>) ->
quote! { quote! {
#doc_comments #doc_comments
pub mod #ident { pub mod #ident {
#[allow(unused_imports)]
use trixy::types::traits::convert_trait::*;
#structures #structures
#enumerations #enumerations
#debug #debug
@ -196,14 +197,6 @@ fn namespace_to_module_enum(namespace: &Namespace) -> TokenStream2 {
} }
} }
fn attribute_to_doc_comment(attribute: &Attribute) -> TokenStream2 {
let Attribute::doc(doc_comment) = attribute;
quote! {
#[doc = #doc_comment]
}
}
fn get_debug_sate() -> TokenStream2 { fn get_debug_sate() -> TokenStream2 {
let debug = DEBUG.with(|d| { let debug = DEBUG.with(|d| {
d.get() d.get()
@ -217,11 +210,11 @@ fn function_to_rust(function: &Function, namespaces: &[&Identifier]) -> TokenStr
let doc_comments: TokenStream2 = function let doc_comments: TokenStream2 = function
.attributes .attributes
.iter() .iter()
.map(attribute_to_doc_comment) .map(|attr| attribute_to_rust(&function.identifier, attr))
.collect(); .collect();
let function_ident = let function_ident =
function_identifier_to_rust(&function, named_type_to_rust, move |r#type| { function_identifier_to_rust(&function, named_type_to_rust, move |r#type| {
let ident = type_to_c_equalivalent(r#type, namespaces); let ident = type_to_c_equivalent(r#type);
quote! { quote! {
trixy::oneshot::Sender<#ident> trixy::oneshot::Sender<#ident>
} }
@ -238,8 +231,9 @@ fn enumeration_to_rust(enumeration: &Enumeration) -> TokenStream2 {
let doc_comments: TokenStream2 = enumeration let doc_comments: TokenStream2 = enumeration
.attributes .attributes
.iter() .iter()
.map(attribute_to_doc_comment) .map(|attr| attribute_to_rust(&enumeration.identifier, attr))
.collect(); .collect();
let ident = identifier_to_rust(&enumeration.identifier); let ident = identifier_to_rust(&enumeration.identifier);
let states: Vec<TokenStream2> = enumeration let states: Vec<TokenStream2> = enumeration
.states .states
@ -249,15 +243,31 @@ fn enumeration_to_rust(enumeration: &Enumeration) -> TokenStream2 {
let debug = get_debug_sate(); let debug = get_debug_sate();
let paired_type = {
let path = type_variant_c_path(&enumeration.identifier.variant);
let ident = mangle_c_type_identifier(&enumeration.identifier);
if path.is_empty() {
quote! {
crate :: #ident
}
} else {
quote! {
#path :: #ident
}
}
};
let convertible = rust_enumeration_into_impl(&enumeration, &paired_type);
quote! { quote! {
#doc_comments #doc_comments
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#debug #debug
#[repr(C)]
#[derive(Convertible)]
pub enum #ident { pub enum #ident {
#(#states),* #(#states),*
} }
#convertible
} }
} }
@ -265,14 +275,30 @@ fn structure_to_rust(structure: &Structure) -> TokenStream2 {
let doc_comments: TokenStream2 = structure let doc_comments: TokenStream2 = structure
.attributes .attributes
.iter() .iter()
.map(attribute_to_doc_comment) .map(|attr| attribute_to_rust(&structure.identifier, attr))
.collect(); .collect();
let ident = identifier_to_rust(&structure.identifier); let ident = identifier_to_rust(&structure.identifier);
let c_ident = {
let path = type_variant_c_path(&structure.identifier.variant);
let ident = mangle_c_type_identifier(&structure.identifier);
if path.is_empty() {
quote! {
crate :: #ident
}
} else {
quote! {
#path :: #ident
}
}
};
let contents: Vec<TokenStream2> = structure let contents: Vec<TokenStream2> = structure
.contents .contents
.iter() .iter()
.map(doc_named_type_to_rust) .map(doc_named_type_to_rust)
.collect(); .collect();
let convertible = structure_convertable_derive(&structure, &c_ident);
let into_impl = structure_into_impl(&structure, &c_ident);
let debug = get_debug_sate(); let debug = get_debug_sate();
@ -280,37 +306,10 @@ fn structure_to_rust(structure: &Structure) -> TokenStream2 {
#doc_comments #doc_comments
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#debug #debug
#[repr(C)]
#[derive(Convertible)]
pub struct #ident { pub struct #ident {
#(#contents),* #(#contents),*
} }
} #convertible
} #into_impl
fn doc_identifier_to_rust(doc_identifier: &DocIdentifier) -> TokenStream2 {
let doc_comments: TokenStream2 = doc_identifier
.attributes
.iter()
.map(attribute_to_doc_comment)
.collect();
let identifier = identifier_to_rust(&doc_identifier.into());
quote! {
#doc_comments
#identifier
}
}
fn doc_named_type_to_rust(doc_named_type: &DocNamedType) -> TokenStream2 {
let doc_comments: TokenStream2 = doc_named_type
.attributes
.iter()
.map(attribute_to_doc_comment)
.collect();
let named_type = named_type_to_rust(&doc_named_type.into());
quote! {
#doc_comments
#named_type
} }
} }

View File

@ -19,13 +19,19 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::ops::Deref;
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use trixy_parser::command_spec::{CommandSpec, Function, Identifier, NamedType, Type}; use trixy_parser::command_spec::{
Attribute, CommandSpec, DocIdentifier, DocNamedType, Function, Identifier, NamedType, Type,
Variant,
};
use crate::config::TrixyConfig; use crate::config::TrixyConfig;
pub mod c_api; pub mod c_api;
pub mod convertible_derive;
pub mod host; pub mod host;
pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 { pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 {
@ -97,20 +103,142 @@ fn named_type_to_rust(named_type: &NamedType) -> TokenStream2 {
} }
fn type_to_rust(r#type: &Type) -> TokenStream2 { fn type_to_rust(r#type: &Type) -> TokenStream2 {
let ident = identifier_to_rust(&r#type.identifier); let ident = identifier_to_rust(&r#type.identifier);
if r#type.generic_args.is_empty() { let namespaces_path = type_variant_rust_path(&r#type.identifier.variant);
if r#type.identifier.name == "str" {
let nasp_path = if let Some(nasp_path) = type_variant_rust_path(&r#type.identifier.variant) {
if nasp_path.is_empty() {
quote! { quote! {
&str crate ::
} }
} else { } else {
let path = namespaces_path;
quote! { quote! {
#ident #path ::
} }
} }
} else {
quote! {}
};
if r#type.generic_args.is_empty() {
quote! {
#nasp_path #ident
}
} else { } else {
let generics: Vec<TokenStream2> = r#type.generic_args.iter().map(type_to_rust).collect(); let generics: Vec<TokenStream2> = r#type.generic_args.iter().map(type_to_rust).collect();
quote! { quote! {
#ident <#(#generics),*> #nasp_path #ident <#(#generics),*>
} }
} }
} }
fn doc_identifier_to_rust(doc_identifier: &DocIdentifier) -> TokenStream2 {
let doc_comments: TokenStream2 = doc_identifier
.attributes
.iter()
.map(|attr| attribute_to_rust(&doc_identifier.into(), attr))
.collect();
let identifier = identifier_to_rust(&doc_identifier.into());
quote! {
#doc_comments
#identifier
}
}
fn doc_named_type_to_rust(doc_named_type: &DocNamedType) -> TokenStream2 {
let doc_comments: TokenStream2 = doc_named_type
.attributes
.iter()
.map(|attr| attribute_to_rust(&&doc_named_type.name, attr))
.collect();
let named_type = named_type_to_rust(&doc_named_type.into());
quote! {
#doc_comments
pub #named_type
}
}
fn attribute_to_rust(_target: &Identifier, attribute: &Attribute) -> TokenStream2 {
match attribute {
Attribute::doc(comment) => quote! {
#[doc = #comment]
},
Attribute::error => quote! {
// We simply use thiserror here
#[derive(trixy::__private::thiserror::Error)]
},
Attribute::msg(msg) => quote! {
#[error(#msg)]
},
Attribute::derive(_) => unimplemented!("Derive is not used as of now"),
}
}
pub fn type_variant_rust_path(variant: &Variant) -> Option<TokenStream2> {
fn namespace_to_borrowed(vec: &Vec<Identifier>) -> Vec<&Identifier> {
let vec_2: Vec<&Identifier> = vec.iter().collect();
vec_2
}
let main_namespace;
match variant {
Variant::Structure { namespace } => {
main_namespace = namespace_to_borrowed(namespace);
}
Variant::Enumeration { namespace } => {
main_namespace = namespace_to_borrowed(namespace);
}
_ => return None,
}
Some(namespaces_to_path(&main_namespace[..]))
}
fn namespaces_to_path<T: Deref<Target = Identifier>>(namespaces: &[T]) -> TokenStream2 {
namespaces
.iter()
.fold(TokenStream2::default(), |acc, nasp| {
if nasp.variant == Variant::RootNamespace && nasp.name == "<root>" {
if acc.is_empty() {
quote! {
crate
}
} else {
unreachable!("An root namespace can never come, after another namespcae")
}
} else {
let ident = format_ident!("{}", nasp.name);
if acc.is_empty() {
quote! {
crate :: #ident
}
} else {
quote! {
#acc :: #ident
}
}
}
})
}
fn namespaces_to_path_expansive(namespaces: &[Identifier]) -> TokenStream2 {
namespaces
.iter()
.fold(TokenStream2::default(), |acc, nasp| {
if nasp.variant == Variant::RootNamespace && nasp.name == "<root>" {
if acc.is_empty() {
quote! {
crate
}
} else {
unreachable!("An root namespace can never come, after another namespcae")
}
} else {
let ident = format_ident!("{}", nasp.name);
if acc.is_empty() {
quote! {
crate :: #ident
}
} else {
quote! {
#acc :: #ident
}
}
}
})
}