dill_impl/
lib.rs

1extern crate proc_macro;
2
3mod types;
4
5use proc_macro::TokenStream;
6use quote::{format_ident, quote};
7use types::InjectionType;
8
9////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10
11struct ComponentParams {
12    vis: syn::Visibility,
13    no_new: bool,
14}
15
16impl syn::parse::Parse for ComponentParams {
17    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
18        let mut params = ComponentParams {
19            vis: syn::Visibility::Inherited,
20            no_new: false,
21        };
22
23        while !input.is_empty() {
24            if input.peek(syn::Token![pub]) {
25                params.vis = input.parse()?;
26            } else {
27                let ident = input.parse::<syn::Ident>()?;
28                match ident.to_string().as_str() {
29                    "no_new" => params.no_new = true,
30                    s => {
31                        return Err(syn::Error::new(
32                            ident.span(),
33                            format!("Unexpected parameter: {s}"),
34                        ));
35                    }
36                }
37            }
38
39            if !input.is_empty() {
40                input.parse::<syn::Token![,]>()?; // Consume the comma
41            }
42        }
43        Ok(params)
44    }
45}
46
47////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
48
49#[proc_macro_attribute]
50pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
51    let params = syn::parse_macro_input!(attr as ComponentParams);
52
53    let ast: syn::Item = syn::parse(item).unwrap();
54    match ast {
55        syn::Item::Struct(struct_ast) => component_from_struct(params, struct_ast),
56        syn::Item::Impl(impl_ast) => component_from_impl(params, impl_ast),
57        _ => {
58            panic!("The #[component] macro can only be used on struct definition or an impl block")
59        }
60    }
61}
62
63////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
64
65#[proc_macro_attribute]
66pub fn scope(_args: TokenStream, item: TokenStream) -> TokenStream {
67    item
68}
69
70////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
71
72#[proc_macro_attribute]
73pub fn interface(_args: TokenStream, item: TokenStream) -> TokenStream {
74    item
75}
76
77////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
78
79#[proc_macro_attribute]
80pub fn meta(_args: TokenStream, item: TokenStream) -> TokenStream {
81    item
82}
83
84////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
85
86fn component_from_struct(params: ComponentParams, mut ast: syn::ItemStruct) -> TokenStream {
87    let impl_name = &ast.ident;
88    let impl_type = syn::parse2(quote! { #impl_name }).unwrap();
89    let impl_generics = syn::parse2(quote! {}).unwrap();
90
91    let args: Vec<_> = ast
92        .fields
93        .iter_mut()
94        .map(|f| {
95            (
96                f.ident.clone().unwrap(),
97                f.ty.clone(),
98                extract_attr_explicit(&mut f.attrs),
99            )
100        })
101        .collect();
102
103    let scope_type =
104        get_scope(&ast.attrs).unwrap_or_else(|| syn::parse_str("::dill::Transient").unwrap());
105
106    let interfaces = get_interfaces(&ast.attrs);
107    let meta = get_meta(&ast.attrs);
108
109    let mut stream: TokenStream = quote! { #ast }.into();
110
111    if !params.no_new {
112        stream.extend(implement_new(&impl_type, &args));
113    }
114
115    let builder: TokenStream = implement_builder(
116        &ast.vis,
117        &impl_type,
118        &impl_generics,
119        scope_type,
120        interfaces,
121        meta,
122        args,
123        !params.no_new,
124    );
125
126    stream.extend(builder);
127    stream
128}
129
130////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
131
132fn component_from_impl(params: ComponentParams, mut ast: syn::ItemImpl) -> TokenStream {
133    let impl_generics = &ast.generics;
134    let impl_type = &ast.self_ty;
135    let new = get_new(&mut ast.items).expect(
136        "When using #[component] macro on the impl block it's expected to contain a new() \
137         function. Otherwise use #[derive(Builder)] on the struct.",
138    );
139
140    let args: Vec<_> = new
141        .sig
142        .inputs
143        .iter_mut()
144        .map(|arg| match arg {
145            syn::FnArg::Typed(targ) => targ,
146            _ => panic!("Unexpected argument in new() function"),
147        })
148        .map(|arg| {
149            (
150                match arg.pat.as_ref() {
151                    syn::Pat::Ident(ident) => ident.ident.clone(),
152                    _ => panic!("Unexpected format of arguments in new() function"),
153                },
154                arg.ty.as_ref().clone(),
155                extract_attr_explicit(&mut arg.attrs),
156            )
157        })
158        .collect();
159
160    let scope_type =
161        get_scope(&ast.attrs).unwrap_or_else(|| syn::parse_str("::dill::Transient").unwrap());
162
163    let interfaces = get_interfaces(&ast.attrs);
164    let meta = get_meta(&ast.attrs);
165
166    let mut stream: TokenStream = quote! { #ast }.into();
167    let builder: TokenStream = implement_builder(
168        &params.vis,
169        impl_type,
170        impl_generics,
171        scope_type,
172        interfaces,
173        meta,
174        args,
175        true,
176    );
177
178    stream.extend(builder);
179    stream
180}
181
182////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
183
184#[allow(clippy::too_many_arguments)]
185fn implement_new(impl_type: &syn::Type, args: &[(syn::Ident, syn::Type, bool)]) -> TokenStream {
186    let arg_decl = args.iter().map(|(name, ty, _)| quote! {#name: #ty});
187    let arg_name = args.iter().map(|(name, _, _)| name);
188
189    quote! {
190        impl #impl_type {
191            #[allow(clippy::too_many_arguments)]
192            pub fn new(
193                #(#arg_decl),*
194            ) -> Self {
195                Self {
196                    #(#arg_name),*
197                }
198            }
199        }
200    }
201    .into()
202}
203
204////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
205
206#[allow(clippy::too_many_arguments)]
207fn implement_builder(
208    impl_vis: &syn::Visibility,
209    impl_type: &syn::Type,
210    _impl_generics: &syn::Generics,
211    scope_type: syn::Path,
212    interfaces: Vec<syn::Type>,
213    meta: Vec<syn::ExprStruct>,
214    args: Vec<(syn::Ident, syn::Type, bool)>,
215    has_new: bool,
216) -> TokenStream {
217    let builder_name = format_ident!("{}Builder", quote! { #impl_type }.to_string());
218
219    let arg_name: Vec<_> = args.iter().map(|(name, _, _)| name).collect();
220
221    let meta_provide: Vec<_> = meta
222        .iter()
223        .enumerate()
224        .map(|(i, e)| implement_meta_provide(i, e))
225        .collect();
226    let meta_vars: Vec<_> = meta
227        .iter()
228        .enumerate()
229        .map(|(i, e)| implement_meta_var(i, e))
230        .collect();
231
232    let mut arg_override_fn_field = Vec::new();
233    let mut arg_override_fn_field_ctor = Vec::new();
234    let mut arg_override_setters = Vec::new();
235    let mut arg_prepare_dependency = Vec::new();
236    let mut arg_provide_dependency = Vec::new();
237    let mut arg_dependency_info = Vec::new();
238
239    for (name, typ, is_explicit) in &args {
240        let (
241            override_fn_field,
242            override_fn_field_ctor,
243            override_setters,
244            prepare_dependency,
245            provide_dependency,
246            dependency_info,
247        ) = implement_arg(name, typ, &builder_name, &scope_type, *is_explicit);
248
249        arg_override_fn_field.push(override_fn_field);
250        arg_override_fn_field_ctor.push(override_fn_field_ctor);
251        arg_override_setters.push(override_setters);
252        arg_prepare_dependency.push(prepare_dependency);
253        arg_provide_dependency.push(provide_dependency);
254        arg_dependency_info.push(dependency_info);
255    }
256
257    arg_override_fn_field.retain(|t| !t.is_empty());
258    arg_override_fn_field_ctor.retain(|t| !t.is_empty());
259    arg_override_setters.retain(|t| !t.is_empty());
260    arg_prepare_dependency.retain(|t| !t.is_empty());
261    arg_provide_dependency.retain(|t| !t.is_empty());
262    arg_dependency_info.retain(|t| !t.is_empty());
263
264    let explicit_arg_decl: Vec<_> = args
265        .iter()
266        .filter(|(_, _, is_explicit)| *is_explicit)
267        .map(|(ident, ty, _)| quote! { #ident: #ty })
268        .collect();
269    let explicit_arg_provide: Vec<_> = args
270        .iter()
271        .filter(|(_, _, is_explicit)| *is_explicit)
272        .map(|(ident, _, _)| quote! { #ident })
273        .collect();
274
275    let ctor = if !has_new {
276        quote! {
277            #impl_type {
278                #( #arg_name: #arg_provide_dependency, )*
279            }
280        }
281    } else {
282        quote! {
283            #impl_type::new(#( #arg_provide_dependency, )*)
284        }
285    };
286
287    let component_or_explicit_factory = if explicit_arg_decl.is_empty() {
288        quote! {
289            impl ::dill::Component for #impl_type {
290                type Impl = #impl_type;
291                type Builder = #builder_name;
292
293                fn builder() -> Self::Builder {
294                    #builder_name::new()
295                }
296            }
297        }
298    } else {
299        quote! {
300            impl #impl_type {
301                #[allow(clippy::too_many_arguments)]
302                pub fn builder(
303                    #(#explicit_arg_decl),*
304                ) -> #builder_name {
305                    #builder_name::new(
306                        #(#explicit_arg_provide),*
307                    )
308                }
309            }
310        }
311    };
312
313    let builder = quote! {
314        #impl_vis struct #builder_name {
315            dill_builder_scope: #scope_type,
316            #(#arg_override_fn_field),*
317        }
318
319        impl #builder_name {
320            #( #meta_vars )*
321
322            pub fn new(
323                #(#explicit_arg_decl),*
324            ) -> Self {
325                Self {
326                    dill_builder_scope: #scope_type::new(),
327                    #(#arg_override_fn_field_ctor),*
328                }
329            }
330
331            #( #arg_override_setters )*
332
333            fn build(&self, cat: &::dill::Catalog, ctx: &::dill::InjectionContext) -> Result<#impl_type, ::dill::InjectionError> {
334                let ctx_build = ctx.push_build(self);
335                let ctx = &ctx_build;
336
337                use ::dill::DependencySpec;
338                #( #arg_prepare_dependency )*
339                Ok(#ctor)
340            }
341        }
342
343        impl ::dill::Builder for #builder_name {
344            fn instance_type(&self) -> ::dill::TypeInfo {
345                ::dill::TypeInfo::of::<#impl_type>()
346            }
347
348            fn scope_type(&self) -> ::dill::TypeInfo {
349                ::dill::TypeInfo::of::<#scope_type>()
350            }
351
352            fn interfaces(&self, clb: &mut dyn FnMut(&::dill::TypeInfo) -> bool) {
353                #(
354                    if !clb(&::dill::TypeInfo::of::<#interfaces>()) { return }
355                )*
356            }
357
358            fn dependencies(&self, clb: &mut dyn FnMut(&::dill::DependencyInfo) -> bool) {
359                #(
360                    if !clb(& #arg_dependency_info) { return }
361                )*
362            }
363
364            fn metadata<'a>(&'a self, clb: & mut dyn FnMut(&'a dyn std::any::Any) -> bool) {
365                #( #meta_provide )*
366            }
367
368            fn get_any(&self, cat: &::dill::Catalog, ctx: &::dill::InjectionContext) -> Result<::std::sync::Arc<dyn ::std::any::Any + Send + Sync>, ::dill::InjectionError> {
369                Ok(::dill::TypedBuilder::get_with_context(self, cat, ctx)?)
370            }
371        }
372
373        impl ::dill::TypedBuilder<#impl_type> for #builder_name {
374            fn get_with_context(&self, cat: &::dill::Catalog, ctx: &::dill::InjectionContext) -> Result<std::sync::Arc<#impl_type>, ::dill::InjectionError> {
375                use ::dill::Scope;
376
377                let inst = self.dill_builder_scope.get_or_create(cat, || {
378                    let inst = self.build(cat, ctx)?;
379                    Ok(::std::sync::Arc::new(inst))
380                })?;
381
382                Ok(inst.downcast().unwrap())
383            }
384
385            fn bind_interfaces(&self, cat: &mut ::dill::CatalogBuilder) {
386                #(
387                    cat.bind::<#interfaces, #impl_type>();
388                )*
389            }
390        }
391
392        #(
393            // Allows casting TypedBuilder<T> into TypedBuilder<dyn I> for all declared interfaces
394            impl ::dill::TypedBuilderCast<#interfaces> for #builder_name
395            {
396                fn cast(self) -> impl ::dill::TypedBuilder<#interfaces> {
397                    struct _B(#builder_name);
398
399                    impl ::dill::Builder for _B {
400                        fn instance_type(&self) -> ::dill::TypeInfo {
401                            self.0.instance_type()
402                        }
403                        fn scope_type(&self) -> ::dill::TypeInfo {
404                            self.0.scope_type()
405                        }
406                        fn interfaces(&self, clb: &mut dyn FnMut(&::dill::TypeInfo) -> bool) {
407                            self.0.interfaces(clb)
408                        }
409                        fn dependencies(&self, clb: &mut dyn FnMut(&::dill::DependencyInfo) -> bool) {
410                            self.0.dependencies(clb)
411                        }
412                        fn metadata<'a>(&'a self, clb: &mut dyn FnMut(&'a dyn std::any::Any) -> bool) {
413                            self.0.metadata(clb)
414                        }
415                        fn get_any(&self, cat: &::dill::Catalog, ctx: &::dill::InjectionContext) -> Result<std::sync::Arc<dyn std::any::Any + Send + Sync>, ::dill::InjectionError> {
416                            self.0.get_any(cat, ctx)
417                        }
418                    }
419
420                    impl ::dill::TypedBuilder<#interfaces> for _B {
421                        fn get_with_context(&self, cat: &::dill::Catalog, ctx: &::dill::InjectionContext) -> Result<::std::sync::Arc<#interfaces>, ::dill::InjectionError> {
422                            match self.0.get_with_context(cat, ctx) {
423                                Ok(v) => Ok(v),
424                                Err(e) => Err(e),
425                            }
426                        }
427
428                        fn bind_interfaces(&self, cat: &mut ::dill::CatalogBuilder) {
429                            self.0.bind_interfaces(cat);
430                        }
431                    }
432
433                    _B(self)
434                }
435            }
436        )*
437    };
438
439    quote! {
440        #component_or_explicit_factory
441
442        #builder
443    }
444    .into()
445}
446
447////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
448
449fn implement_arg(
450    name: &syn::Ident,
451    typ: &syn::Type,
452    builder: &syn::Ident,
453    scope_type: &syn::Path,
454    is_explicit: bool,
455) -> (
456    proc_macro2::TokenStream, // override_fn_field
457    proc_macro2::TokenStream, // override_fn_field_ctor
458    proc_macro2::TokenStream, // override_setters
459    proc_macro2::TokenStream, // prepare_dependency
460    proc_macro2::TokenStream, // provide_dependency
461    proc_macro2::TokenStream, // dependency_info
462) {
463    let override_fn_name = format_ident!("arg_{}_fn", name);
464
465    let injection_type = if is_explicit {
466        InjectionType::Value { typ: typ.clone() }
467    } else {
468        types::deduce_injection_type(typ)
469    };
470
471    // Used to declare the field that stores the override factory function or
472    // an explicit argument
473    let override_fn_field = if is_explicit {
474        quote! { #name: #typ }
475    } else {
476        match &injection_type {
477            InjectionType::Reference { .. }
478            | InjectionType::Catalog
479            | InjectionType::CatalogRef
480            | InjectionType::CatalogWeakRef => proc_macro2::TokenStream::new(),
481            _ => quote! {
482                #override_fn_name: Option<Box<dyn Fn(&::dill::Catalog) -> Result<#typ, ::dill::InjectionError> + Send + Sync>>
483            },
484        }
485    };
486
487    // Used initialize the field that stores the override factory function or
488    // an explicit argument
489    let override_fn_field_ctor = if is_explicit {
490        quote! { #name: #name }
491    } else {
492        match &injection_type {
493            InjectionType::Reference { .. }
494            | InjectionType::Catalog
495            | InjectionType::CatalogRef
496            | InjectionType::CatalogWeakRef => proc_macro2::TokenStream::new(),
497            _ => quote! { #override_fn_name: None },
498        }
499    };
500
501    // Used to create with_* and with_*_fn setters for dependency overrides
502    let override_setters = if is_explicit {
503        proc_macro2::TokenStream::new()
504    } else {
505        match &injection_type {
506            InjectionType::Reference { .. }
507            | InjectionType::Catalog
508            | InjectionType::CatalogRef
509            | InjectionType::CatalogWeakRef => proc_macro2::TokenStream::new(),
510            _ => {
511                let setter_val_name = format_ident!("with_{}", name);
512                let setter_fn_name = format_ident!("with_{}_fn", name);
513                quote! {
514                    pub fn #setter_val_name(mut self, val: #typ) -> #builder {
515                        self.#override_fn_name = Some(Box::new(move |_| Ok(val.clone())));
516                        self
517                    }
518
519                    pub fn #setter_fn_name(
520                        mut self,
521                        fun: impl Fn(&::dill::Catalog) -> Result<#typ, ::dill::InjectionError> + 'static + Send + Sync
522                    ) -> #builder {
523                        self.#override_fn_name = Some(Box::new(fun));
524                        self
525                    }
526                }
527            }
528        }
529    };
530
531    // Used in TBuilder::build() to extract the dependency from the catalog
532    let prepare_dependency = if is_explicit {
533        proc_macro2::TokenStream::new()
534    } else {
535        let do_get_dependency = get_do_get_dependency(&injection_type, scope_type);
536        match &injection_type {
537            InjectionType::Reference { .. }
538            | InjectionType::Catalog
539            | InjectionType::CatalogRef
540            | InjectionType::CatalogWeakRef => {
541                quote! { let #name = #do_get_dependency; }
542            }
543            _ => quote! {
544                let #name = match &self.#override_fn_name {
545                    Some(fun) => fun(cat)?,
546                    _ => #do_get_dependency,
547                };
548            },
549        }
550    };
551
552    // Called to provide dependency value to T's constructor
553    let provide_dependency = if is_explicit {
554        quote! { self.#name.clone() }
555    } else {
556        match &injection_type {
557            InjectionType::Reference { .. } => {
558                quote! { #name.as_ref() }
559            }
560            _ => quote! { #name },
561        }
562    };
563
564    // Called to provide dependency info metadata
565    let dependency_info = if is_explicit {
566        proc_macro2::TokenStream::new()
567    } else {
568        let info = get_do_get_dependency_info(&injection_type);
569        match &injection_type {
570            InjectionType::Reference { .. }
571            | InjectionType::Catalog
572            | InjectionType::CatalogRef
573            | InjectionType::CatalogWeakRef => info,
574            _ => quote! { #info.bound(self.#override_fn_name.is_some()) },
575        }
576    };
577
578    (
579        override_fn_field,
580        override_fn_field_ctor,
581        override_setters,
582        prepare_dependency,
583        provide_dependency,
584        dependency_info,
585    )
586}
587
588////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
589
590fn get_do_get_dependency(
591    injection_type: &InjectionType,
592    scope_type: &syn::Path,
593) -> proc_macro2::TokenStream {
594    match injection_type {
595        InjectionType::Catalog => {
596            if scope_type.segments.last().unwrap().ident != "Transient" {
597                panic!(
598                    "`Catalog` can only be injected by value into components in a `Transient` \
599                     scope as they guarantee a short lifetime. Injecting catalog into other \
600                     scopes may result in cyclic references and resource leaks."
601                );
602            } else {
603                quote! { cat.clone() }
604            }
605        }
606        InjectionType::CatalogRef => quote! { cat },
607        InjectionType::CatalogWeakRef => quote! { cat.weak_ref() },
608        InjectionType::Arc { inner } => {
609            quote! { cat.get_with_context::<::dill::OneOf::<#inner>>(ctx)? }
610        }
611        InjectionType::Reference { inner } => {
612            quote! { cat.get_with_context::<::dill::OneOf::<#inner>>(ctx)? }
613        }
614        InjectionType::Option { element } => match element.as_ref() {
615            InjectionType::Arc { inner } => {
616                quote! { cat.get_with_context::<::dill::Maybe::<::dill::OneOf::<#inner>>>(ctx)? }
617            }
618            InjectionType::Value { typ } => {
619                quote! { cat.get_with_context::<::dill::Maybe::<::dill::OneOf::<#typ>>>(ctx)?.map(|v| v.as_ref().clone()) }
620            }
621            _ => {
622                unimplemented!("Currently only Option<Arc<Iface>> and Option<Value> are supported")
623            }
624        },
625        InjectionType::Lazy { element } => match element.as_ref() {
626            InjectionType::Arc { inner } => {
627                quote! { cat.get_with_context::<::dill::specs::Lazy::<::dill::OneOf::<#inner>>>(ctx)? }
628            }
629            _ => unimplemented!("Currently only Lazy<Arc<Iface>> is supported"),
630        },
631        InjectionType::Vec { item } => match item.as_ref() {
632            InjectionType::Arc { inner } => {
633                quote! { cat.get_with_context::<::dill::AllOf::<#inner>>(ctx)? }
634            }
635            _ => unimplemented!("Currently only Vec<Arc<Iface>> is supported"),
636        },
637        InjectionType::Value { typ } => {
638            quote! { cat.get_with_context::<::dill::OneOf::<#typ>>(ctx).map(|v| v.as_ref().clone())? }
639        }
640    }
641}
642
643fn get_do_get_dependency_info(injection_type: &InjectionType) -> proc_macro2::TokenStream {
644    match injection_type {
645        InjectionType::Catalog | InjectionType::CatalogRef => {
646            quote! { ::dill::DependencyInfo::of::<::dill::Catalog, ::dill::specs::OneOf::<::dill::Catalog>>() }
647        }
648        InjectionType::CatalogWeakRef => {
649            quote! { ::dill::DependencyInfo::of::<::dill::CatalogWeakRef, ::dill::specs::OneOf::<::dill::CatalogWeakRef>>() }
650        }
651        InjectionType::Arc { inner } => quote! {
652            ::dill::DependencyInfo::of::<#inner, ::dill::specs::OneOf::<#inner>>()
653        },
654        InjectionType::Reference { inner } => quote! {
655            ::dill::DependencyInfo::of::<#inner, ::dill::specs::OneOf::<#inner>>()
656        },
657        InjectionType::Option { element } => match element.as_ref() {
658            InjectionType::Arc { inner } => {
659                quote! {
660                    ::dill::DependencyInfo::of::<#inner, ::dill::specs::Maybe::<::dill::OneOf::<#inner>>>()
661                }
662            }
663            InjectionType::Value { typ } => {
664                quote! {
665                    ::dill::DependencyInfo::of::<#typ, ::dill::specs::Maybe::<::dill::OneOf::<#typ>>>()
666                }
667            }
668            _ => {
669                unimplemented!("Currently only Option<Arc<Iface>> and Option<Value> are supported")
670            }
671        },
672        InjectionType::Lazy { element } => match element.as_ref() {
673            InjectionType::Arc { inner } => {
674                quote! {
675                    ::dill::DependencyInfo::of::<#inner, ::dill::specs::Lazy::<::dill::OneOf::<#inner>>>()
676                }
677            }
678            _ => unimplemented!("Currently only Lazy<Arc<Iface>> is supported"),
679        },
680        InjectionType::Vec { item } => match item.as_ref() {
681            InjectionType::Arc { inner } => quote! {
682                ::dill::DependencyInfo::of::<#inner, ::dill::specs::AllOf::<#inner>>()
683            },
684            _ => unimplemented!("Currently only Vec<Arc<Iface>> is supported"),
685        },
686        InjectionType::Value { typ } => {
687            quote! {
688                ::dill::DependencyInfo::of::<#typ, ::dill::specs::OneOf::<#typ>>()
689            }
690        }
691    }
692}
693
694////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
695
696fn implement_meta_var(index: usize, expr: &syn::ExprStruct) -> proc_macro2::TokenStream {
697    let ident = format_ident!("_meta_{index}");
698    let typ = &expr.path;
699    quote! {
700        const #ident: #typ = #expr;
701    }
702}
703
704fn implement_meta_provide(index: usize, _expr: &syn::ExprStruct) -> proc_macro2::TokenStream {
705    let ident = format_ident!("_meta_{index}");
706    quote! {
707        if !clb(&Self::#ident) { return }
708    }
709}
710
711////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
712
713/// Searches for `#[scope(X)]` attribute and returns `X`
714fn get_scope(attrs: &Vec<syn::Attribute>) -> Option<syn::Path> {
715    let mut scope = None;
716
717    for attr in attrs {
718        if is_dill_attr(attr, "scope") {
719            attr.parse_nested_meta(|meta| {
720                scope = Some(meta.path);
721                Ok(())
722            })
723            .expect("Could not parse scope");
724        }
725    }
726
727    scope
728}
729
730////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
731
732/// Searches for all `#[interface(X)]` attributes and returns all types
733fn get_interfaces(attrs: &Vec<syn::Attribute>) -> Vec<syn::Type> {
734    let mut interfaces = Vec::new();
735
736    for attr in attrs {
737        if is_dill_attr(attr, "interface") {
738            let iface = attr.parse_args().unwrap();
739            interfaces.push(iface);
740        }
741    }
742
743    interfaces
744}
745
746////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
747
748/// Searches for all `#[meta(X)]` attributes and returns all expressions
749fn get_meta(attrs: &Vec<syn::Attribute>) -> Vec<syn::ExprStruct> {
750    let mut meta = Vec::new();
751
752    for attr in attrs {
753        if is_dill_attr(attr, "meta") {
754            let expr = attr.parse_args().unwrap();
755            meta.push(expr);
756        }
757    }
758
759    meta
760}
761
762////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
763
764fn is_dill_attr<I: ?Sized>(attr: &syn::Attribute, ident: &I) -> bool
765where
766    syn::Ident: PartialEq<I>,
767{
768    if attr.path().is_ident(ident) {
769        true
770    } else {
771        attr.path().segments.len() == 2
772            && &attr.path().segments[0].ident == "dill"
773            && attr.path().segments[1].ident == *ident
774    }
775}
776
777////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
778
779/// Searches `impl` block for `new()` method
780fn get_new(impl_items: &mut [syn::ImplItem]) -> Option<&mut syn::ImplItemFn> {
781    impl_items
782        .iter_mut()
783        .filter_map(|i| match i {
784            syn::ImplItem::Fn(m) => Some(m),
785            _ => None,
786        })
787        .find(|m| m.sig.ident == "new")
788}
789
790////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
791
792fn extract_attr_explicit(attrs: &mut Vec<syn::Attribute>) -> bool {
793    let mut present = false;
794    attrs.retain_mut(|attr| {
795        if is_attr_explicit(attr) {
796            present = true;
797            false
798        } else {
799            true
800        }
801    });
802    present
803}
804
805fn is_attr_explicit(attr: &syn::Attribute) -> bool {
806    if !is_dill_attr(attr, "component") {
807        return false;
808    }
809    let syn::Meta::List(meta) = &attr.meta else {
810        return false;
811    };
812    meta.tokens.to_string().contains("explicit")
813}