logix_type_derive/
lib.rs

1#![deny(warnings, clippy::all)]
2#![allow(non_snake_case)] // NOTE(2024.03.29): There appear to be a bug triggering this even when set on the Types struct
3mod derive_enum;
4mod derive_struct;
5
6use proc_macro::TokenStream;
7use proc_macro2::TokenStream as TokenStream2;
8use quote::quote;
9use syn::{parse_macro_input, DeriveInput};
10
11#[derive(Clone)]
12struct Types {
13    LogixTypeDescriptor: TokenStream2,
14    LogixValueDescriptor: TokenStream2,
15    LogixType: TokenStream2,
16    LogixVfs: TokenStream2,
17    LogixParser: TokenStream2,
18    ParseResult: TokenStream2,
19    ParseError: TokenStream2,
20    Wanted: TokenStream2,
21    Value: TokenStream2,
22    Token: TokenStream2,
23    Brace: TokenStream2,
24    Delim: TokenStream2,
25}
26
27struct Shared<'a> {
28    prefix: TokenStream2,
29    type_name_str: String,
30    type_name: syn::Ident,
31    types: Types,
32    impl_gen: syn::ImplGenerics<'a>,
33}
34
35/// Derives the LogixType trait
36#[proc_macro_derive(LogixType)]
37pub fn impl_logix_type(input: TokenStream) -> TokenStream {
38    let input = parse_macro_input!(input as DeriveInput);
39
40    let (impl_gen, ty_gen, where_gen) = input.generics.split_for_impl();
41
42    let shared = Shared {
43        prefix: quote!(),
44        type_name_str: input.ident.to_string(),
45        type_name: input.ident,
46        types: Types {
47            LogixTypeDescriptor: quote!(::logix_type::type_trait::LogixTypeDescriptor),
48            LogixValueDescriptor: quote!(::logix_type::type_trait::LogixValueDescriptor),
49            LogixType: quote!(::logix_type::LogixType),
50            LogixVfs: quote!(::logix_vfs::LogixVfs),
51            LogixParser: quote!(::logix_type::LogixParser),
52            ParseResult: quote!(::logix_type::error::Result),
53            ParseError: quote!(::logix_type::error::ParseError),
54            Wanted: quote!(::logix_type::error::Wanted),
55            Value: quote!(::logix_type::type_trait::Value),
56            Token: quote!(::logix_type::token::Token),
57            Brace: quote!(::logix_type::token::Brace),
58            Delim: quote!(::logix_type::token::Delim),
59        },
60        impl_gen,
61    };
62    let Shared {
63        prefix: _,
64        type_name_str,
65        type_name,
66        types:
67            Types {
68                LogixTypeDescriptor,
69                LogixType,
70                LogixVfs,
71                LogixParser,
72                ParseResult,
73                Value,
74                ..
75            },
76        impl_gen,
77    } = &shared;
78
79    let (value_desc, parse) = match input.data {
80        syn::Data::Struct(data) => derive_struct::do_any(&shared, data.fields, false),
81        syn::Data::Enum(data) => derive_enum::do_any(&shared, data.variants),
82        syn::Data::Union(..) => return quote!(compile_error!("Union is not supported")).into(),
83    };
84
85    let descriptor = quote!(
86        #LogixTypeDescriptor {
87            name: #type_name_str,
88            doc: "",
89            value: #value_desc,
90        }
91    );
92
93    let tokens = quote! {
94        impl #impl_gen #LogixType for #type_name #ty_gen #where_gen {
95            fn descriptor() -> &'static #LogixTypeDescriptor{
96                // NOTE(2023.11): Currently generics can't be used to make statics, so I need this work-around
97                static RET: std::sync::OnceLock<#LogixTypeDescriptor> = std::sync::OnceLock::new();
98                RET.get_or_init(|| #descriptor)
99            }
100
101            fn default_value() -> Option<Self> {
102                None
103            }
104
105            fn logix_parse<FS: #LogixVfs>(p: &mut #LogixParser<FS>) -> #ParseResult<#Value<Self>> {
106                #parse
107            }
108        }
109    };
110
111    tokens.into()
112}