shah_macros/
lib.rs

1use syn::parse_quote;
2use utils::traitor::{Traitor, TraitorField};
3
4mod api;
5mod command;
6mod entity;
7mod enum_code;
8mod enum_int;
9mod legacy;
10mod model;
11mod perms;
12mod routes;
13mod schema;
14mod utils;
15
16use proc_macro::TokenStream;
17
18/// Example:
19/// ```ignore
20/// #[derive(Debug, Default, shah::Command)]
21/// enum MyCommands {
22///     #[default]
23///     Help,
24///     Run,
25///     Master { gene: shah::models::Gene }
26/// }
27/// ```
28#[proc_macro_derive(Command)]
29pub fn command(code: TokenStream) -> TokenStream {
30    command::command(code)
31}
32
33/// Example:
34/// ```ignore
35/// #[derive(Debug, shah::EnumCode)]
36/// #[enum_code(u8)]
37/// pub enum Schema {
38///     Model(Model), // 0u8
39///     Array { len: usize }, // 1u8
40///     U8,  // 2u8
41///     Gene // 3u8
42/// }
43/// ```
44#[proc_macro_derive(EnumCode, attributes(enum_code))]
45pub fn enum_code(code: TokenStream) -> TokenStream {
46    enum_code::enum_code(code)
47}
48
49/// Example:
50/// ```ignore
51/// #[shah::legacy]
52/// mod items {
53///
54///     /// the derive macros from **Base** are set for all children
55///     #[derive(Debug, Serialize, Deserialize, ToSchema)]
56///     pub struct Base {
57///         gene: Gene,
58///         is_alive: bool,
59///     }
60///
61///     impl From<&Review> for Base {
62///         fn from(value: &Review) -> Self {
63///             Self {
64///                 gene: value.gene,
65///                 is_alive: value.is_alive(),
66///             }
67///         }
68///     }
69///
70///     // child 1
71///     pub struct ReviewInfo {
72///         // gene: Gene,
73///         // is_alive: bool,
74///         user: Gene,
75///     }
76///
77///     impl From<&Review> for ReviewInfo {
78///         fn from(value: &Review) -> Self {
79///             Self { user: value.user }
80///         }
81///     }
82///
83///     // child 2
84///     pub struct EateryReviewInfo {
85///         // gene: Gene,
86///         // is_alive: bool,
87///         user: Option<EateryReviewUserInfo>,
88///     }
89///
90///     impl From<&Review> for EateryReviewInfo {
91///         fn from(review: &Review) -> Self {
92///             Self { user: None }
93///         }
94///     }
95/// }
96/// ```
97#[proc_macro_attribute]
98pub fn legacy(_args: TokenStream, code: TokenStream) -> TokenStream {
99    let item = syn::parse_macro_input!(code as syn::ItemMod);
100    legacy::legacy(item).unwrap_or_else(syn::Error::into_compile_error).into()
101}
102
103/// enum_ini is a two way conversion enum `<->` u16
104/// default **start** is `0` and default **ty** is `u8`
105/// Example:
106/// ```ignore
107/// #[shah::enum_int(u16)]
108/// #[derive(Debug, Default, Clone, Copy)]
109/// pub enum ExampleError {
110///     #[default]
111///     Unknown = 0,
112///     UserNotFound,
113///     BadPhone,
114///     BadStr,
115/// }
116/// ```
117#[proc_macro_attribute]
118pub fn enum_int(args: TokenStream, code: TokenStream) -> TokenStream {
119    let item = syn::parse_macro_input!(code as syn::ItemEnum);
120    enum_int::enum_int(item, args)
121        .unwrap_or_else(syn::Error::into_compile_error)
122        .into()
123}
124
125#[proc_macro_attribute]
126pub fn api(args: TokenStream, code: TokenStream) -> TokenStream {
127    type Args = syn::punctuated::Punctuated<syn::MetaNameValue, syn::Token![,]>;
128    let item = syn::parse_macro_input!(code as syn::ItemMod);
129    let attrs = syn::parse_macro_input!(args with Args::parse_terminated);
130
131    api::api(attrs, item).unwrap_or_else(syn::Error::into_compile_error).into()
132}
133
134#[proc_macro_attribute]
135pub fn model(_args: TokenStream, code: TokenStream) -> TokenStream {
136    let item = syn::parse_macro_input!(code as syn::ItemStruct);
137    model::model(item).unwrap_or_else(syn::Error::into_compile_error).into()
138}
139
140#[proc_macro_derive(Entity, attributes(entity))]
141/// Derive macro generating an impl of the trait `Entity`.
142///
143/// You can use `#[entity(gene)]`, `#[entity(flags)]` and `#[entity(growth)]`
144/// to set custom fields for these methods.
145pub fn entity(code: TokenStream) -> TokenStream {
146    entity::entity(code)
147}
148
149#[proc_macro_derive(Belt, attributes(belt))]
150/// Derive macro generating an impl of the trait `Belt`.
151/// You can use `#[belt(next)]`, `#[belt(past)]` and `#[belt(buckle)]`
152/// to set custom fields for these methods.
153pub fn belt(code: TokenStream) -> TokenStream {
154    let ci = crate_ident();
155    let inp = syn::parse_macro_input!(code as syn::DeriveInput);
156
157    let gene = parse_quote!(#ci::models::Gene);
158
159    let tr = Traitor::new(
160        "belt",
161        parse_quote!(#ci::db::belt::Belt),
162        [
163            TraitorField::new("next", &gene, false),
164            TraitorField::new("past", &gene, false),
165            TraitorField::new("buckle", &gene, false),
166        ],
167    );
168    tr.derive(inp).unwrap_or_else(syn::Error::into_compile_error).into()
169}
170
171#[proc_macro_derive(Buckle, attributes(buckle))]
172pub fn buckle(code: TokenStream) -> TokenStream {
173    let ci = crate_ident();
174    let inp = syn::parse_macro_input!(code as syn::DeriveInput);
175
176    let gene = parse_quote!(#ci::models::Gene);
177    let pu64 = parse_quote!(u64);
178
179    let tr = Traitor::new(
180        "buckle",
181        parse_quote!(#ci::db::belt::Buckle),
182        [
183            TraitorField::new("head", &gene, false),
184            TraitorField::new("tail", &gene, false),
185            TraitorField::new("belt_count", &pu64, true),
186        ],
187    );
188    tr.derive(inp).unwrap_or_else(syn::Error::into_compile_error).into()
189}
190
191#[proc_macro_derive(Duck, attributes(duck))]
192pub fn duck(code: TokenStream) -> TokenStream {
193    let ci = crate_ident();
194    let inp = syn::parse_macro_input!(code as syn::DeriveInput);
195    let gene = parse_quote!(#ci::models::Gene);
196    let tr = Traitor::new(
197        "duck",
198        parse_quote!(#ci::db::pond::Duck),
199        [TraitorField::new("pond", &gene, false)],
200    );
201    tr.derive(inp).unwrap_or_else(syn::Error::into_compile_error).into()
202}
203
204#[proc_macro_derive(Pond, attributes(pond))]
205pub fn pond(code: TokenStream) -> TokenStream {
206    let ci = crate_ident();
207    let inp = syn::parse_macro_input!(code as syn::DeriveInput);
208
209    let gene = parse_quote!(#ci::models::Gene);
210    let pru8 = parse_quote!(u8);
211    let gene_id = parse_quote!(#ci::models::GeneId);
212
213    let tr = Traitor::new(
214        "pond",
215        parse_quote!(#ci::db::pond::Pond),
216        [
217            TraitorField::new("next", &gene, false),
218            TraitorField::new("past", &gene, false),
219            TraitorField::new("origin", &gene, false),
220            TraitorField::new("stack", &gene_id, true),
221            TraitorField::new("alive", &pru8, true),
222            TraitorField::new("empty", &pru8, true),
223        ],
224    );
225    tr.derive(inp).unwrap_or_else(syn::Error::into_compile_error).into()
226}
227
228#[proc_macro_derive(Origin, attributes(origin))]
229pub fn origin(code: TokenStream) -> TokenStream {
230    let ci = crate_ident();
231    let inp = syn::parse_macro_input!(code as syn::DeriveInput);
232
233    let gene = parse_quote!(#ci::models::Gene);
234    let pu64 = parse_quote!(u64);
235
236    let tr = Traitor::new(
237        "origin",
238        parse_quote!(#ci::db::pond::Origin),
239        [
240            TraitorField::new("head", &gene, false),
241            TraitorField::new("tail", &gene, false),
242            TraitorField::new("pond_count", &pu64, true),
243            TraitorField::new("item_count", &pu64, true),
244        ],
245    );
246    tr.derive(inp).unwrap_or_else(syn::Error::into_compile_error).into()
247}
248
249#[proc_macro_derive(ShahSchema)]
250pub fn schema(code: TokenStream) -> TokenStream {
251    schema::schema(code)
252}
253
254#[proc_macro]
255pub fn routes(code: TokenStream) -> TokenStream {
256    routes::routes(code)
257}
258
259#[proc_macro]
260pub fn perms(code: TokenStream) -> TokenStream {
261    perms::perms(code)
262}
263
264fn crate_ident() -> syn::Ident {
265    // let found_crate = crate_name("shah").unwrap();
266    // let name = match &found_crate {
267    //     FoundCrate::Itself => "shah",
268    //     FoundCrate::Name(name) => name,
269    // };
270    ident!("shah")
271}
272
273macro_rules! ident {
274    ($name:literal) => {
275        syn::Ident::new($name, proc_macro2::Span::call_site())
276    };
277    ($name:expr) => {
278        syn::Ident::new($name, proc_macro2::Span::call_site())
279    };
280}
281pub(crate) use ident;
282
283macro_rules! err {
284    ($span:expr, $($msg:literal),*) => {
285        Err(syn::Error::new($span, concat!( $($msg),* )))
286    };
287    ($span:expr, $msg:expr) => {
288        Err(syn::Error::new($span, $msg))
289    };
290}
291
292pub(crate) use err;