fix(macros): Generate namespacesed types and convertible impls

This commit is contained in:
Benedikt Peetz 2024-03-24 21:06:14 +01:00
parent 5fe757f829
commit 8b02d13dae
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 quote::{format_ident, quote};
use trixy_parser::command_spec::{
Attribute, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
Namespace, Structure,
CommandSpec, Enumeration, Function, Identifier, Namespace, Structure,
};
use crate::{
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();}
@ -66,13 +73,13 @@ thread_local! {static DEBUG: OnceCell<TokenStream2> = OnceCell::new();}
/// }
/// pub mod trinitrix {
/// #[allow(non_camel_case_types)]
/// #[derive(Debug, Convertible)]
/// #[derive(Debug)]
/// struct Callback {
/// func: String,
/// timeout: String,
/// }
/// #[allow(non_camel_case_types)]
/// #[derive(Debug, Convertible)]
/// #[derive(Debug)]
/// enum CallbackPriority {
/// High,
/// Medium,
@ -122,9 +129,6 @@ pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 {
let debug = get_debug_sate();
quote! {
#[allow(unused_imports)]
use trixy::types::traits::convert_trait::*;
#structures
#enumerations
#debug
@ -145,7 +149,7 @@ fn namespace_to_module(namespace: &Namespace, namespaces: &Vec<&Identifier>) ->
let doc_comments: TokenStream2 = namespace
.attributes
.iter()
.map(attribute_to_doc_comment)
.map(|attr| attribute_to_rust(&namespace.name, attr))
.collect();
let structures: TokenStream2 = namespace.structures.iter().map(structure_to_rust).collect();
let enumerations: TokenStream2 = namespace
@ -173,9 +177,6 @@ fn namespace_to_module(namespace: &Namespace, namespaces: &Vec<&Identifier>) ->
quote! {
#doc_comments
pub mod #ident {
#[allow(unused_imports)]
use trixy::types::traits::convert_trait::*;
#structures
#enumerations
#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 {
let debug = DEBUG.with(|d| {
d.get()
@ -217,11 +210,11 @@ fn function_to_rust(function: &Function, namespaces: &[&Identifier]) -> TokenStr
let doc_comments: TokenStream2 = function
.attributes
.iter()
.map(attribute_to_doc_comment)
.map(|attr| attribute_to_rust(&function.identifier, attr))
.collect();
let function_ident =
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! {
trixy::oneshot::Sender<#ident>
}
@ -238,8 +231,9 @@ fn enumeration_to_rust(enumeration: &Enumeration) -> TokenStream2 {
let doc_comments: TokenStream2 = enumeration
.attributes
.iter()
.map(attribute_to_doc_comment)
.map(|attr| attribute_to_rust(&enumeration.identifier, attr))
.collect();
let ident = identifier_to_rust(&enumeration.identifier);
let states: Vec<TokenStream2> = enumeration
.states
@ -249,15 +243,31 @@ fn enumeration_to_rust(enumeration: &Enumeration) -> TokenStream2 {
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! {
#doc_comments
#[allow(non_camel_case_types)]
#debug
#[repr(C)]
#[derive(Convertible)]
pub enum #ident {
#(#states),*
}
#convertible
}
}
@ -265,14 +275,30 @@ fn structure_to_rust(structure: &Structure) -> TokenStream2 {
let doc_comments: TokenStream2 = structure
.attributes
.iter()
.map(attribute_to_doc_comment)
.map(|attr| attribute_to_rust(&structure.identifier, attr))
.collect();
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
.contents
.iter()
.map(doc_named_type_to_rust)
.collect();
let convertible = structure_convertable_derive(&structure, &c_ident);
let into_impl = structure_into_impl(&structure, &c_ident);
let debug = get_debug_sate();
@ -280,37 +306,10 @@ fn structure_to_rust(structure: &Structure) -> TokenStream2 {
#doc_comments
#[allow(non_camel_case_types)]
#debug
#[repr(C)]
#[derive(Convertible)]
pub struct #ident {
#(#contents),*
}
}
}
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
#convertible
#into_impl
}
}

View File

@ -19,13 +19,19 @@
* If not, see <https://www.gnu.org/licenses/>.
*/
use std::ops::Deref;
use proc_macro2::TokenStream as TokenStream2;
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;
pub mod c_api;
pub mod convertible_derive;
pub mod host;
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 {
let ident = identifier_to_rust(&r#type.identifier);
if r#type.generic_args.is_empty() {
if r#type.identifier.name == "str" {
let namespaces_path = type_variant_rust_path(&r#type.identifier.variant);
let nasp_path = if let Some(nasp_path) = type_variant_rust_path(&r#type.identifier.variant) {
if nasp_path.is_empty() {
quote! {
&str
crate ::
}
} else {
let path = namespaces_path;
quote! {
#ident
#path ::
}
}
} else {
quote! {}
};
if r#type.generic_args.is_empty() {
quote! {
#nasp_path #ident
}
} else {
let generics: Vec<TokenStream2> = r#type.generic_args.iter().map(type_to_rust).collect();
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
}
}
}
})
}