Skip to main content

entid_derive/
lib.rs

1/* Copyright © 2025, CosmicMind, Inc. */
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, DeriveInput, Expr, Lit};
6
7/// Attribute for setting the prefix for an entity
8#[proc_macro_attribute]
9pub fn prefix(_attr: TokenStream, item: TokenStream) -> TokenStream {
10    // This is just a marker attribute, so we return the item unchanged
11    item
12}
13
14/// Attribute for setting the delimiter for an entity
15#[proc_macro_attribute]
16pub fn delimiter(_attr: TokenStream, item: TokenStream) -> TokenStream {
17    // This is just a marker attribute, so we return the item unchanged
18    item
19}
20
21/// Derive macro for implementing the `Prefix` trait
22///
23/// # Attributes
24///
25/// - `#[entid(prefix = "...")]` - Sets the prefix for the entity (required)
26/// - `#[entid(delimiter = "...")]` - Sets the delimiter for the entity (optional, defaults to "_")
27///
28/// # Example
29///
30/// ```rust
31/// use entid::Prefix;
32///
33/// #[derive(Prefix)]
34/// #[entid(prefix = "user", delimiter = "_")]
35/// struct User;
36///
37/// #[derive(Prefix)]
38/// #[entid(prefix = "post", delimiter = "-")]
39/// struct Post;
40///
41/// // The delimiter is optional and defaults to "_"
42/// #[derive(Prefix)]
43/// #[entid(prefix = "comment")]
44/// struct Comment;
45/// ```
46#[proc_macro_derive(Prefix, attributes(entid))]
47pub fn derive_prefix(input: TokenStream) -> TokenStream {
48    let input = parse_macro_input!(input as DeriveInput);
49    let name = &input.ident;
50
51    // Default values
52    let mut prefix = format!("{}_", name).to_lowercase();
53    let mut delimiter = String::from("_");
54
55    // Parse attributes
56    for attr in &input.attrs {
57        if attr.path().is_ident("entid") {
58            attr.parse_nested_meta(|meta| {
59                if meta.path.is_ident("prefix") {
60                    let value = meta.value()?;
61                    let expr: Expr = value.parse()?;
62                    if let Expr::Lit(expr_lit) = expr {
63                        if let Lit::Str(lit_str) = expr_lit.lit {
64                            prefix = lit_str.value();
65                        }
66                    }
67                } else if meta.path.is_ident("delimiter") {
68                    let value = meta.value()?;
69                    let expr: Expr = value.parse()?;
70                    if let Expr::Lit(expr_lit) = expr {
71                        if let Lit::Str(lit_str) = expr_lit.lit {
72                            delimiter = lit_str.value();
73                        }
74                    }
75                }
76                Ok(())
77            })
78            .ok();
79        }
80    }
81
82    let gen = quote! {
83        impl entid::Prefix for #name {
84            fn prefix() -> &'static str {
85                #prefix
86            }
87
88            fn delimiter() -> &'static str {
89                #delimiter
90            }
91        }
92    };
93
94    gen.into()
95}