kmdparse_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_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_parse_enum;
19
20type DeriveResult = Result<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    Ok(parse_tokens)
27}
28
29fn derive_enum<'a>(ctx: &mut CodegenContext<'a>, data: &'a syn::DataEnum) -> DeriveResult {
30    let variantset = VariantsSet::from_variants(ctx, data.variants.iter())?;
31    let parse_tokens = gen_parse_enum(ctx, &variantset);
32    Ok(parse_tokens)
33}
34
35fn derive<'a>(ctx: &mut CodegenContext<'a>, input: &'a syn::DeriveInput) -> DeriveResult {
36    let type_attributes = TypeAttributes::from_attributes(input.attrs.iter())?;
37    ctx.context_type = type_attributes.context_type;
38
39    match &input.data {
40        syn::Data::Struct(data) => derive_struct(ctx, data),
41        syn::Data::Enum(data) => derive_enum(ctx, data),
42        syn::Data::Union(data) => Err(syn::Error::new(
43            data.union_token.span(),
44            "parsing unions is not supported",
45        )),
46    }
47}
48
49#[proc_macro_derive(Parsable, attributes(cmd))]
50pub fn derive_parseable(input: TokenStream) -> TokenStream {
51    let input = syn::parse_macro_input!(input as syn::DeriveInput);
52    let name = &input.ident;
53
54    let mut context = CodegenContext::from_derive_input(&input);
55    let result = derive(&mut context, &input);
56
57    match result {
58        Ok(parse_impl) => gen::implementation(name, &context, parse_impl).into(),
59        Err(error) => error.into_compile_error().into(),
60    }
61}