Skip to main content

paperless_api_macros/
lib.rs

1mod derive_base;
2mod repr_serde;
3
4use proc_macro::TokenStream;
5use quote::{format_ident, quote};
6use syn::{DeriveInput, parse_macro_input};
7
8use crate::derive_base::BaseStruct;
9
10/// Derives a `Create..` struct for the given input struct.
11#[proc_macro_derive(CreateDto, attributes(dto, api_info))]
12pub fn derive_create_dto(input: TokenStream) -> TokenStream {
13    // Parse the input
14    let input = parse_macro_input!(input as DeriveInput);
15    let input_struct = match BaseStruct::try_from(input) {
16        Ok(val) => val,
17        Err(e) => return e.to_compile_error().into(),
18    };
19
20    // Generate the new struct
21    let new_struct_name = format_ident!("Create{}", input_struct.name);
22    let new_struct = input_struct.generate_new_struct(&new_struct_name, false);
23
24    let id_type_name = input_struct.id_type;
25    let id_type_name = quote!(crate::id::#id_type_name);
26
27    let endpoint = input_struct.endpoint.clone();
28    let name = input_struct.name;
29
30    // Generate the final output with the trait implementation
31    TokenStream::from(quote! {
32        #new_struct
33
34        #[automatically_derived]
35        impl crate::dto::CreateDto for #new_struct_name {
36            type Id = #id_type_name;
37            type BaseType = #name;
38
39            fn endpoint() -> &'static str {
40                #endpoint
41            }
42        }
43    })
44}
45
46/// Derives a `Update..` struct for the given input struct.
47#[proc_macro_derive(UpdateDto, attributes(dto, api_info))]
48pub fn derive_update_dto(input: TokenStream) -> TokenStream {
49    // Parse the input
50    let input = parse_macro_input!(input as DeriveInput);
51    let input_struct = match BaseStruct::try_from(input) {
52        Ok(val) => val,
53        Err(e) => return e.to_compile_error().into(),
54    };
55
56    // Generate the new struct
57    let new_struct_name = format_ident!("Update{}", input_struct.name);
58    let new_struct = input_struct.generate_new_struct(&new_struct_name, true);
59
60    let id_type_name = input_struct.id_type;
61    let id_type_name = quote!(crate::id::#id_type_name);
62
63    let endpoint = input_struct.endpoint.clone();
64    let name = input_struct.name;
65
66    // Generate the final output with the trait implementation
67    TokenStream::from(quote! {
68        #new_struct
69
70        #[automatically_derived]
71        impl crate::dto::UpdateDto for #new_struct_name {
72            type Id = #id_type_name;
73            type BaseType = #name;
74
75            fn endpoint() -> &'static str {
76                #endpoint
77            }
78        }
79    })
80}
81
82/// Derives `Item` trait for the given input struct.
83#[proc_macro_derive(Item, attributes(dto, api_info))]
84pub fn derive_item_trait(input: TokenStream) -> TokenStream {
85    // Parse the input
86    let input = parse_macro_input!(input as DeriveInput);
87    let input_struct = match BaseStruct::try_from(input) {
88        Ok(val) => val,
89        Err(e) => return e.to_compile_error().into(),
90    };
91
92    let id_type_name = format_ident!("{}Id", input_struct.name);
93    let id_type_name = quote!(crate::id::#id_type_name);
94
95    let endpoint = input_struct.endpoint.clone();
96    let name = input_struct.name;
97
98    // Generate the final output with the trait implementation
99    TokenStream::from(quote! {
100        #[automatically_derived]
101        impl crate::dto::Item for #name {
102            type Id = #id_type_name;
103            type BaseType = #name;
104
105            fn endpoint() -> &'static str {
106                #endpoint
107            }
108
109            fn id(&self) -> Self::Id {
110                self.id
111            }
112        }
113    })
114}
115
116/// Derives `Serialize` and `Deserialize` by reading discriminant values
117/// directly from the AST.
118///
119/// The enum must have exactly one data-carrying variant (e.g. `Unknown(u8)`)
120/// which is used as the fallback for unknown values.
121#[proc_macro_derive(ReprSerde)]
122pub fn derive_repr_serde(input: TokenStream) -> TokenStream {
123    let input = parse_macro_input!(input as DeriveInput);
124
125    let repr_enum = match repr_serde::ReprEnum::try_from(input) {
126        Ok(val) => val,
127        Err(e) => return e.to_compile_error().into(),
128    };
129
130    TokenStream::from(repr_enum.generate())
131}