patternutils_derive/
lib.rs1use 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#[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 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 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 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 if meta.path.is_ident("name") {
239 let lit: LitStr = meta.value()?.parse()?;
240 name = lit.value().clone();
241 return Ok(());
242 }
243
244 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#[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}