plotly_derive/
lib.rs

1#![recursion_limit = "256"]
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::parse::Parser;
6use syn::DeriveInput;
7use syn::{parse_macro_input, ItemStruct};
8mod field_setter;
9
10#[proc_macro_derive(FieldSetter, attributes(field_setter))]
11pub fn field_setter(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
12    let input = syn::parse::<DeriveInput>(input).unwrap();
13    field_setter::field_setter_impl(input)
14}
15
16#[proc_macro_attribute]
17pub fn layout_structs(_attr: TokenStream, item: TokenStream) -> TokenStream {
18    let input = parse_macro_input!(item as ItemStruct);
19    let vis = &input.vis;
20    let fields = &input.fields;
21    let attrs = &input.attrs;
22
23    // Generate LayoutTemplate (no template field, no #[field_setter(kind =
24    // "layout")])
25    let template_struct_name = format_ident!("LayoutTemplate");
26    let template_struct = quote! {
27        #(#attrs)*
28        #[serde_with::skip_serializing_none]
29        #[derive(Serialize, Debug, Clone, FieldSetter)]
30        #vis struct #template_struct_name #fields
31    };
32
33    // Generate Layout (with template field and #[field_setter(kind = "layout")])
34    let layout_struct_name = format_ident!("Layout");
35    let mut layout_fields = fields.clone();
36    // Add the template field
37    if let syn::Fields::Named(ref mut named) = layout_fields {
38        named.named.push(
39            syn::Field::parse_named
40                .parse2(quote! {
41                    #[field_setter(skip)]
42                    template: Option<Box<std::borrow::Cow<'static, Template>>>
43                })
44                .unwrap(),
45        );
46    }
47    let layout_struct = quote! {
48        #(#attrs)*
49        #[serde_with::skip_serializing_none]
50        #[derive(Serialize, Debug, Clone, FieldSetter)]
51        #[field_setter(kind = "layout")]
52        #vis struct #layout_struct_name #layout_fields
53    };
54
55    // Generate impls (add your methods as needed)
56    let template_impl = quote! {
57        impl #template_struct_name {
58            pub fn new() -> Self { Default::default() }
59            pub fn add_annotation(&mut self, annotation: Annotation) {
60                if self.annotations.is_none() { self.annotations = Some(Vec::new()); }
61                self.annotations.as_mut().unwrap().push(annotation);
62            }
63            pub fn add_shape(&mut self, shape: Shape) {
64                if self.shapes.is_none() { self.shapes = Some(Vec::new()); }
65                self.shapes.as_mut().unwrap().push(shape);
66            }
67        }
68    };
69    let layout_impl = quote! {
70        impl #layout_struct_name {
71            pub fn new() -> Self { Default::default() }
72            pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap() }
73            pub fn add_annotation(&mut self, annotation: Annotation) {
74                if self.annotations.is_none() { self.annotations = Some(Vec::new()); }
75                self.annotations.as_mut().unwrap().push(annotation);
76            }
77            pub fn add_shape(&mut self, shape: Shape) {
78                if self.shapes.is_none() { self.shapes = Some(Vec::new()); }
79                self.shapes.as_mut().unwrap().push(shape);
80            }
81            pub fn template<T>(mut self, template: T) -> Self
82            where T: Into<std::borrow::Cow<'static, Template>> {
83                self.template = Some(Box::new(template.into()));
84                self
85            }
86        }
87    };
88
89    let expanded = quote! {
90        #template_struct
91        #layout_struct
92        #template_impl
93        #layout_impl
94    };
95    TokenStream::from(expanded)
96}