cmdparse_derive/
lib.rs

1mod attributes;
2mod context;
3mod fields;
4mod fields_gen;
5mod gen;
6mod variants;
7mod variants_gen;
8
9use attributes::{BuildableAttributes, TypeAttributes};
10use context::CodegenContext;
11use fields::FieldsSet;
12use fields_gen::{gen_complete_struct, gen_parse_struct};
13use proc_macro::TokenStream;
14use proc_macro2::TokenStream as TokenStream2;
15use quote::quote;
16use syn::spanned::Spanned;
17use variants::VariantsSet;
18use variants_gen::{gen_complete_enum, gen_parse_enum};
19
20type DeriveResult = Result<(TokenStream2, TokenStream2), syn::Error>;
21
22fn derive_struct<'a>(ctx: &mut CodegenContext<'a>, data: &'a syn::DataStruct) -> DeriveResult {
23    let fieldset = FieldsSet::from_fields(ctx, &data.fields)?;
24    let name = ctx.type_name;
25    let parse_tokens = gen_parse_struct(quote! { #name }, ctx, &fieldset, None);
26    let complete_tokens = gen_complete_struct(ctx, &fieldset, None);
27    Ok((parse_tokens, complete_tokens))
28}
29
30fn derive_enum<'a>(ctx: &mut CodegenContext<'a>, data: &'a syn::DataEnum) -> DeriveResult {
31    let variantset = VariantsSet::from_variants(ctx, data.variants.iter())?;
32    let parse_tokens = gen_parse_enum(ctx, &variantset);
33    let complete_tokens = gen_complete_enum(ctx, &variantset);
34    Ok((parse_tokens, complete_tokens))
35}
36
37fn derive<'a>(ctx: &mut CodegenContext<'a>, input: &'a syn::DeriveInput) -> DeriveResult {
38    let type_attributes = TypeAttributes::from_attributes(input.attrs.iter())?;
39    ctx.context_type = type_attributes.context_type;
40
41    match &input.data {
42        syn::Data::Struct(data) => derive_struct(ctx, data),
43        syn::Data::Enum(data) => derive_enum(ctx, data),
44        syn::Data::Union(data) => Err(syn::Error::new(
45            data.union_token.span(),
46            "parsing unions is not supported",
47        )),
48    }
49}
50
51#[proc_macro_derive(Parsable, attributes(cmd))]
52pub fn derive_parseable(input: TokenStream) -> TokenStream {
53    let input = syn::parse_macro_input!(input as syn::DeriveInput);
54    let name = &input.ident;
55
56    let mut context = CodegenContext::from_derive_input(&input);
57    let result = derive(&mut context, &input);
58
59    match result {
60        Ok((parse_impl, complete_impl)) => {
61            gen::implementation(name, &context, parse_impl, complete_impl).into()
62        }
63        Err(error) => error.into_compile_error().into(),
64    }
65}