patternutils_derive/
lib.rs

1use proc_macro::TokenStream;
2use syn::{
3    parse::{Parse, ParseStream, Result},
4    parse_macro_input,
5    punctuated::Punctuated,
6    Data, DeriveInput, Fields, Ident, ItemTrait, Lit, LitBool, LitStr, Meta, Token, Visibility,
7};
8
9#[derive(Debug, Default)]
10struct ObserverArgs {
11    publisher_name: Option<String>
12}
13
14impl Parse for ObserverArgs {
15    fn parse(input: ParseStream) -> Result<Self> {
16        let mut result: ObserverArgs = ObserverArgs::default();
17        if input.is_empty() {
18            return Ok(result);
19        }
20
21        let metas = Punctuated::<Meta, Token![,]>::parse_terminated(input)?;
22
23        for meta in metas {
24            let meta = match meta {
25                Meta::NameValue(n) => n,
26                _ => {
27                    return Err(syn::Error::new_spanned(meta, "Expected name = value pairs"));
28                }
29            };
30
31            let ident = match meta.path.get_ident() {
32                Some(n) => n,
33                None => {
34                    return Err(syn::Error::new_spanned(meta.path, "Expected identifier"));
35                }
36            };
37
38            if let syn::Expr::Lit(expr_lit) = meta.value {
39                match ident.to_string().as_str() {
40                    "publisher_name" => {
41                        if let Lit::Str(lit_str) = expr_lit.lit {
42                            result.publisher_name = Some(lit_str.value());
43                            continue;
44                        }
45
46                        return Err(syn::Error::new_spanned(
47                            meta.path,
48                            "Expected type 'String' for 'publisher_name' but found something else.",
49                        ));
50                    }
51                    _ => {
52                        return Err(syn::Error::new_spanned(
53                            meta.path,
54                            "Unknown attribute. Expected 'publisher_name'",
55                        ))
56                    }
57                }
58            }
59        }
60
61        Ok(result)
62    }
63}
64
65
66/// Derives a builder pattern for the struct.
67///
68/// #### Container Attribute: `#[builder_attr(...)]`
69///
70/// - `name = "BuilderName"`: Renames the generated builder struct.
71/// - `build_by = "value" | "reference"`: Chooses between value-consuming or reference-based build method.
72/// - `opt_in`: Requires fields to be explicitly marked with `#[builder_field(include = true)]`.
73///
74/// #### Field Attribute: `#[builder_field(...)]`
75///
76/// - `name = "method_name"`: Renames the generated builder method for this field.
77/// - `include = true | false`: Controls whether the field is included in the builder (overrides `opt_in`).
78///
79/// # Example
80/// ```rust
81/// #[derive(Builder)]
82/// #[builder_attr(build_by = "reference", opt_in)]
83/// struct User {
84///     #[builder_field(name = "set_name", include = true)]
85///     name: String,
86///     password: String,
87/// }
88/// ```
89/// 
90/// To be included in the builder pattern, a field must implement the `Default` trait.
91/// Fields not included in the builder must be explicitly provided as arguments to the final `build` method.
92/// 
93/// ```rust
94/// #[derive(Builder)]
95/// #[builder_attr(name = "PersonMaker")]
96/// struct Person{
97///     weight: f32,
98///     age: u16,
99///     name: &'static str,
100/// 
101///     #[builder_field(include = false)]
102///     callback: fn(i32) -> i32,
103/// }
104/// 
105/// let person = Person::builder()
106///     .age(37)
107///     .weight(72.45)
108///     .name("Tenma")
109///     .build(|num| num + 5);
110/// 
111/// assert_eq!(person.age, 37);
112/// assert_eq!(person.weight, 72.45);
113/// assert_eq!(person.name, "Tenma");
114/// assert_eq!((person.callback)(5), 10);
115/// ```
116#[proc_macro_derive(Builder, attributes(builder_attr, builder_field))]
117pub fn builder_derive_macro(item: TokenStream) -> TokenStream {
118    let astree: DeriveInput = match syn::parse(item) {
119        Ok(n) => n,
120        Err(_) => {
121            let msg = "An error occured.";
122            return quote::quote! {
123                compile_error!(#msg);
124            }
125            .into();
126        }
127    };
128
129    let mut custom_builder_name: Option<String> = None;
130    let mut build_method: String = String::from("value");
131    let mut default_exclude = false;
132
133    for attr in astree.attrs {
134        if attr.path().is_ident("builder_attr") {
135            match attr.parse_nested_meta(|meta| {
136
137                // #builder_attr(name = N)
138                if meta.path.is_ident("name") {
139                    let lit: LitStr = meta.value()?.parse()?;
140                    custom_builder_name = Some(lit.value());
141                    return Ok(());
142                }
143
144                // #builder_attr(build_by = N)
145                if meta.path.is_ident("build_by") {
146                    let lit: LitStr = meta.value()?.parse()?;
147                    let method_val = lit.value();
148                    match method_val.as_str() {
149                        "reference" | "value" => {
150                            build_method = method_val;
151                        },
152                        _ => {
153                            return Err(meta.error("`build_by` attribute can get only one of `reference | value` as parameter."));
154                        }
155                    }
156                    return Ok(());
157                }
158
159                // #builder_attr(opt_in)
160                if meta.path.is_ident("opt_in") {
161                    default_exclude = true;
162                    return Ok(());
163                }
164
165                Err(meta.error("unrecognized attribute"))
166            }) {
167                Ok(_) => {}
168                Err(e) => {
169                    let msg = e.to_string();
170                    return quote::quote! {
171                    compile_error!(#msg);
172                    }
173                    .into();
174                }
175            }
176        }
177    }
178
179    let original_ident = astree.ident;
180    let builder_ident = match custom_builder_name {
181        Some(n) => syn::Ident::new(&n, original_ident.span()),
182        None => syn::Ident::new(&format!("{}Builder", original_ident), original_ident.span()),
183    };
184
185    let data_structure = match &astree.data {
186        Data::Struct(n) => n,
187        _ => {
188            return quote::quote! {
189                compile_error!("`#[derive(Builder)]` can only be used with structs.");
190            }
191            .into();
192        }
193    };
194
195    let fields = match &data_structure.fields {
196        Fields::Named(n) => n.named.clone(),
197        Fields::Unit => {
198            return quote::quote! {
199                compile_error!("`#[derive(Builder)]` cannot be used with unit structs.");
200            }
201            .into();
202        }
203        Fields::Unnamed(_) => {
204            return quote::quote! {
205                compile_error!("`#[derive(Builder)]` cannot be used with tuple structs.");
206            }
207            .into();
208        }
209    };
210
211    let mut included_field_names: Vec<syn::Ident> = vec![];
212    let mut excluded_field_names: Vec<syn::Ident> = vec![];
213
214    let mut included_original_field_names: Vec<syn::Ident> = vec![];
215    let mut excluded_original_field_names: Vec<syn::Ident> = vec![];
216
217    let mut included_field_types: Vec<&syn::Type> = vec![];
218    let mut excluded_field_types: Vec<&syn::Type> = vec![];
219
220    for field in fields.iter() {
221        let original_name = match field.ident.as_ref() {
222            Some(n) => n.to_string(),
223            None => {
224                let msg = "Error occured";
225                return quote::quote! {
226                    compile_error!(#msg)
227                }
228                .into();
229            }
230        };
231        let mut name = original_name.clone();
232        let mut include: Option<bool> = None;
233
234        for attr in field.attrs.iter() {
235            if attr.path().is_ident("builder_field") {
236                match attr.parse_nested_meta(|meta| {
237                    // #builder_field(name = "N")
238                    if meta.path.is_ident("name") {
239                        let lit: LitStr = meta.value()?.parse()?;
240                        name = lit.value().clone();
241                        return Ok(());
242                    }
243
244                    // #builder_field(include = true | false)
245                    if meta.path.is_ident("include") {
246                        let lit: LitBool = meta.value()?.parse()?;
247                        include = Some(lit.value());
248
249                        return Ok(());
250                    }
251
252                    Err(meta.error("unrecognized attribute"))
253                }) {
254                    Ok(_) => {}
255                    Err(e) => {
256                        let msg = e.to_string();
257                        return quote::quote! {
258                            compile_error!(#msg);
259                        }
260                        .into();
261                    }
262                }
263            }
264        }
265
266        let span = match field.ident.as_ref() {
267            Some(n) => n.span(),
268            None => {
269                return quote::quote! {
270                    compile_error!("An error occured");
271                }
272                .into();
273            }
274        };
275
276        let should_include = match include {
277            Some(n) => n,
278            None => !default_exclude,
279        };
280
281        if should_include {
282            included_field_names.push(syn::Ident::new(&name, span));
283            included_original_field_names.push(syn::Ident::new(&original_name, span));
284            included_field_types.push(&field.ty);
285        } else {
286            excluded_field_names.push(syn::Ident::new(&name, span));
287            excluded_original_field_names.push(syn::Ident::new(&original_name, span));
288            excluded_field_types.push(&field.ty);
289        }
290    }
291
292    let mut expanded = quote::quote! {
293        impl #original_ident{
294
295            #[doc = "Creates and returns a new instance of the builder for this struct."]
296            fn builder() -> #builder_ident{
297                #builder_ident::default()
298            }
299        }
300
301        #[doc = "The builder struct derives the `Default` trait, so every included field must also implement `Default`."]
302        #[derive(Default)]
303        pub struct #builder_ident {
304            #(#included_field_names: #included_field_types),*
305        }
306    };
307
308    if build_method == "value" {
309        expanded.extend(quote::quote! {
310            impl #builder_ident{
311                #(
312                    pub fn #included_field_names(self, #included_field_names:#included_field_types) -> Self {
313                        #builder_ident{
314                            #included_field_names:#included_field_names,
315                            ..self
316                        }
317                    }
318                )*
319
320                pub fn build(self, #( #excluded_field_names : #excluded_field_types ),* ) -> #original_ident{
321                    #original_ident {
322                        #(
323                            #included_original_field_names: self.#included_field_names.clone(),
324                        )*
325                        #(
326                            #excluded_original_field_names: #excluded_field_names,
327                        )*
328                    }
329                }
330            }
331        });
332    } else {
333        expanded.extend(quote::quote! {
334            impl #builder_ident{
335                #(
336                    pub fn #included_field_names(&mut self, #included_field_names:#included_field_types) -> &mut Self {
337                        self.#included_field_names = #included_field_names;
338                        self
339                    }
340                )*
341
342                #[doc = "The `build` method constructs the entire struct using the builder's internal values for included fields. Any remaining fields must be provided as arguments to this method."]
343                pub fn build(&self, #( #excluded_field_names : #excluded_field_types ),*) -> #original_ident{
344                    #original_ident {
345                        #(
346                            #included_original_field_names: self.#included_field_names.clone(),
347                        )*
348                        #(
349                            #excluded_original_field_names: #excluded_field_names.clone(),
350                        )*
351                    }
352                }
353            }
354        });
355    }
356
357    expanded.into()
358}
359
360/// Implements the `event(&mut self)` & `as_any(&self)` methodes to applied trait
361/// and generate Observer struct to use observer pattern.
362///
363/// ### Supported Attributes:
364/// - `publisher_name = "name"` = change the name of the generated publisher struct to spesific value.
365/// 
366/// ### Generates
367/// (for trait `MyTrait`)
368///
369/// ```rust
370/// trait MyTrait{
371///     ...
372///     fn event(&mut self);
373///     fn as_any(&self) -> &dyn Any;
374/// }
375///
376/// struct MyTraitObserver{
377///     observers: Vec<Box<dyn MyTrait>>
378/// }
379///
380/// impl MyTraitObserver{
381///     fn new() -> MyTraitObserver {...}
382///     fn get_observer_ptr(observer: &Box<dyn MyTrait>) -> *const dyn MyTrait {...}
383///
384///     fn subscribe(&mut self, val: Box<dyn MyTrait>) {...}
385///     fn unsubscribe(&mut self, target: *const dyn MyTrait) {...}
386///     fn notify(&mut self) {...}
387/// }
388/// ```
389///
390/// # Example
391///
392/// ```rust
393/// use patternutils::observer;
394///
395/// #[observer(publisher_name = "ItemPublisher")]
396/// trait ItemObserver {
397///     fn get_code(&self) -> usize;
398/// }
399///
400/// struct Item {
401///     code: usize
402/// }
403///
404/// impl ItemObserver for Item {
405///     fn get_code(&self) -> usize {
406///         self.code
407///     }
408///
409///     fn event(&mut self) {
410///         println!("[{}] Event!", self.code);
411///     }
412///
413///     fn as_any(&self) ->  &dyn Any {
414///         self
415///     }
416/// }
417///
418/// fn main() {
419///     let mut last_ptr = None;
420///     let mut publisher = ItemPublisher::new();
421///
422///     for i in 0..10{
423///         let boxed_item: Box<dyn ItemObserver> = Box::new(Item {code: i});
424///         last_ptr = Some(ItemPublisher::get_observer_ptr(&boxed_item));
425///         publisher.subscribe(boxed_item);            //Add instance to observers
426///     }
427///     
428///     println!("Every item:");
429///     publisher.notify();                             //Notify all instances
430///     
431///     println!("\nEvery item without the last one:");
432///
433///     publisher.unsubscribe(last_ptr.unwrap());       //Remove last added instance
434///     publisher.notify();                             //Notify all instances
435/// }
436/// ```
437#[proc_macro_attribute]
438pub fn observer(attr: TokenStream, item: TokenStream) -> TokenStream {
439    let mut input = parse_macro_input!(item as ItemTrait);
440    let args = parse_macro_input!(attr as ObserverArgs);
441
442    let pub_state = match input.vis {
443        Visibility::Public(_) => Some(quote::quote! {pub}),
444        _ => None,
445    };
446
447    let trait_name = &input.ident;
448    let publisher_name: Ident = match args.publisher_name {
449        Some(n) => Ident::new(&n, trait_name.span()),
450        None => Ident::new(
451            &(trait_name.to_string() + &"Publisher".to_owned()),
452            trait_name.span(),
453        ),
454    };
455
456    let event_doc_line = format!(
457        "Internal method which will invoked when its publisher (`{}`) calls `notify()` method.",
458        publisher_name
459    );
460
461    let methods = quote::quote! {
462        #[doc = #event_doc_line]
463        fn event(&mut self);
464        fn as_any(&self) -> &dyn ::std::any::Any;
465    };
466
467    let dummy_trait: syn::ItemTrait = syn::parse2(quote::quote! {
468        trait __Dummy {
469            #methods
470        }
471    })
472    .expect("Failed to parse methods");
473    input.items.extend(dummy_trait.items);
474
475    let publisher_struct_doc = format!("Publisher struct of instances which implements `{}`. This can be used to send invoke messages, add and remove observers.", trait_name);
476
477    quote::quote! {
478        #input
479
480        #[doc = #publisher_struct_doc]
481        #[doc = "# Example"]
482        #[doc = "```rust"]
483        #[doc = "let mut publisher = MyPublisher::new();"]
484        #[doc = "publisher.subscribe(Box::new(my_var));"]
485        #[doc = "publisher.notify();"]
486        #[doc = ""]
487        #[doc = "```"]
488        #pub_state struct #publisher_name{
489            observers: Vec<Box<dyn #trait_name>>
490        }
491
492        impl #publisher_name{
493            #[doc = "Push the boxed instance into observers list."]
494            #pub_state fn subscribe(&mut self, val: Box<dyn #trait_name>){
495                self.observers.push(val);
496            }
497
498            #[doc = "Remove the instance by its boxed pointer reference from the observers list."]
499            #[doc = ""]
500            #[doc = "# Example"]
501            #[doc = "```rust"]
502            #[doc = "use patternutils::observer;"]
503            #[doc = ""]
504            #[doc = "#[observer(publisher_name = \"FooPublisher\")]"]
505            #[doc = "trait FooObserver {}"]
506            #[doc = "struct Foo {code: usize}"]
507            #[doc = "impl FooObserver for Foo {"]
508            #[doc = "   fn event(&mut self) {"]
509            #[doc = "       println!(\"[{}] Event!\", self.code);"]
510            #[doc = "   }"]
511            #[doc = ""]
512            #[doc = "   fn as_any(&self) -> &dyn ::std::any::Any {"]
513            #[doc = "       self"]
514            #[doc = "   }"]
515            #[doc = "}"]
516            #[doc = ""]
517            #[doc = "let mut publisher = FooPublisher::new();"]
518            #[doc = "let instance = Foo{code: 3};"]
519            #[doc = ""]
520            #[doc = "let boxed_val: Box<dyn FooObserver> = Box::new(instance);"]
521            #[doc = "let ptr = FooPublisher::get_observer_ptr(&boxed_val);"]
522            #[doc = ""]
523            #[doc = "publisher.subscribe(boxed_val);"]
524            #[doc = ""]
525            #[doc = "println!(\"before\");"]
526            #[doc = "publisher.notify();"]
527            #[doc = "publisher.unsubscribe(ptr);"]
528            #[doc = ""]
529            #[doc = "println!(\"after\");"]
530            #[doc = "publisher.notify();"]
531            #[doc = "```"]
532            #pub_state fn unsubscribe(&mut self, target: *const dyn #trait_name){
533                self.observers.retain(|obs| {
534                    let ptr: *const dyn #trait_name = &**obs;
535                    ptr != target
536                });
537            }
538
539            #[doc = "Invoke every instance which are observing this publisher."]
540            #pub_state fn notify(&mut self){
541                for item in self.observers.iter_mut() {
542                    item.event();
543                }
544            }
545
546            #pub_state fn new() -> #publisher_name {
547                #publisher_name{
548                    observers: vec![]
549                }
550            }
551
552            #[doc = "Get pointer referece of boxed trait implentation."]
553            #pub_state fn get_observer_ptr(observer: &Box<dyn #trait_name>) -> *const dyn #trait_name {
554                &**observer
555            }
556        }
557    }
558    .into()
559}