ai_json_template_derive/
lib.rs

1// ---------------- [ File: ai-json-template-derive/src/lib.rs ]
2#[macro_use] mod imports; use imports::*;
3
4xp!{gather_doc_comments}
5xp!{comma_separated_expression}
6xp!{classify_field_type}
7
8#[proc_macro_derive(AiJsonTemplate)]
9pub fn derive_ai_json_template(input: TokenStream) -> TokenStream {
10    trace!("Entering derive_ai_json_template macro.");
11
12    let ast = parse_macro_input!(input as DeriveInput);
13    let struct_ident = &ast.ident;
14    let struct_span  = ast.span();
15    let struct_name_str = struct_ident.to_string();
16    trace!("Processing struct: {}", struct_name_str);
17
18    // Gather doc comments from the struct itself
19    let struct_docs_vec = gather_doc_comments(&ast.attrs);
20    let struct_docs_str = struct_docs_vec.join("\n");
21    trace!("Gathered struct doc comments = {:?}", struct_docs_vec);
22
23    let fields = match &ast.data {
24        Data::Struct(DataStruct { fields: Fields::Named(named), .. }) => {
25            trace!("Struct has named fields.");
26            &named.named
27        },
28        _ => {
29            let err = syn::Error::new(
30                struct_span,
31                "AiJsonTemplate derive only supports a named struct."
32            );
33            return err.to_compile_error().into();
34        }
35    };
36
37    let mut field_inits = Vec::new();
38    for field in fields {
39        let field_ident = match &field.ident {
40            Some(id) => id,
41            None => {
42                let err = syn::Error::new(
43                    field.span(),
44                    "Unnamed fields are not supported by AiJsonTemplate."
45                );
46                return err.to_compile_error().into();
47            }
48        };
49        let field_name_str = field_ident.to_string();
50        trace!("Analyzing field: {}", field_name_str);
51
52        // doc comments for the field
53        let field_docs = gather_doc_comments(&field.attrs).join("\n");
54        trace!("Field docs => {:?}", field_docs);
55
56        let ty = &field.ty;
57        let type_q = quote!(#ty).to_string();
58        trace!("Field type => {}", type_q);
59
60        if let Some(expr) = classify_field_type(ty, &field_docs) {
61            field_inits.push(quote! {
62                map.insert(#field_name_str.to_string(), #expr);
63            });
64        } else {
65            let err_msg = format!("Unsupported field type for AiJsonTemplate: {}", type_q);
66            trace!("ERROR: {}", err_msg);
67            let err = syn::Error::new(ty.span(), err_msg);
68            return err.to_compile_error().into();
69        }
70    }
71
72    let expanded = quote! {
73        impl AiJsonTemplate for #struct_ident {
74            fn to_template() -> serde_json::Value {
75                tracing::trace!("AiJsonTemplate::to_template for struct {}", #struct_name_str);
76
77                let mut root = serde_json::Map::new();
78                root.insert("struct_docs".to_string(), serde_json::Value::String(#struct_docs_str.to_string()));
79                root.insert("struct_name".to_string(), serde_json::Value::String(#struct_name_str.to_string()));
80
81                let mut map = serde_json::Map::new();
82                #(#field_inits)*
83
84                root.insert("fields".to_string(), serde_json::Value::Object(map));
85                serde_json::Value::Object(root)
86            }
87        }
88    };
89
90    trace!("Exiting derive_ai_json_template macro for {}", struct_name_str);
91    expanded.into()
92}