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_check_dependency = 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            check_dependency,
247        ) = implement_arg(name, typ, &builder_name, *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_check_dependency.push(check_dependency);
255    }
256
257    let explicit_arg_decl: Vec<_> = args
258        .iter()
259        .filter(|(_, _, is_explicit)| *is_explicit)
260        .map(|(ident, ty, _)| quote! { #ident: #ty })
261        .collect();
262    let explicit_arg_provide: Vec<_> = args
263        .iter()
264        .filter(|(_, _, is_explicit)| *is_explicit)
265        .map(|(ident, _, _)| quote! { #ident })
266        .collect();
267
268    let ctor = if !has_new {
269        quote! {
270            #impl_type {
271                #( #arg_name: #arg_provide_dependency, )*
272            }
273        }
274    } else {
275        quote! {
276            #impl_type::new(#( #arg_provide_dependency, )*)
277        }
278    };
279
280    let component_or_explicit_factory = if explicit_arg_decl.is_empty() {
281        quote! {
282            impl ::dill::Component for #impl_type {
283                type Impl = #impl_type;
284                type Builder = #builder_name;
285
286                fn builder() -> Self::Builder {
287                    #builder_name::new()
288                }
289            }
290        }
291    } else {
292        quote! {
293            impl #impl_type {
294                #[allow(clippy::too_many_arguments)]
295                pub fn builder(
296                    #(#explicit_arg_decl),*
297                ) -> #builder_name {
298                    #builder_name::new(
299                        #(#explicit_arg_provide),*
300                    )
301                }
302            }
303        }
304    };
305
306    let builder = quote! {
307        #impl_vis struct #builder_name {
308            dill_builder_scope: #scope_type,
309            #(#arg_override_fn_field),*
310        }
311
312        impl #builder_name {
313            #( #meta_vars )*
314
315            pub fn new(
316                #(#explicit_arg_decl),*
317            ) -> Self {
318                Self {
319                    dill_builder_scope: #scope_type::new(),
320                    #(#arg_override_fn_field_ctor),*
321                }
322            }
323
324            #( #arg_override_setters )*
325
326            fn build(&self, cat: &::dill::Catalog) -> Result<#impl_type, ::dill::InjectionError> {
327                use ::dill::DependencySpec;
328                #( #arg_prepare_dependency )*
329                Ok(#ctor)
330            }
331        }
332
333        impl ::dill::Builder for #builder_name {
334            fn instance_type_id(&self) -> ::std::any::TypeId {
335                ::std::any::TypeId::of::<#impl_type>()
336            }
337
338            fn instance_type_name(&self) -> &'static str {
339                ::std::any::type_name::<#impl_type>()
340            }
341
342            fn interfaces(&self, clb: &mut dyn FnMut(&::dill::InterfaceDesc) -> bool) {
343                #(
344                    if !clb(&::dill::InterfaceDesc {
345                        type_id: ::std::any::TypeId::of::<#interfaces>(),
346                        type_name: ::std::any::type_name::<#interfaces>(),
347                    }) { return }
348                )*
349            }
350
351            fn metadata<'a>(&'a self, clb: & mut dyn FnMut(&'a dyn std::any::Any) -> bool) {
352                #( #meta_provide )*
353            }
354
355            fn get_any(&self, cat: &::dill::Catalog) -> Result<::std::sync::Arc<dyn ::std::any::Any + Send + Sync>, ::dill::InjectionError> {
356                Ok(::dill::TypedBuilder::get(self, cat)?)
357            }
358
359            fn check(&self, cat: &::dill::Catalog) -> Result<(), ::dill::ValidationError> {
360                use ::dill::DependencySpec;
361
362                let mut errors = Vec::new();
363                #(
364                if let Err(err) = #arg_check_dependency {
365                    errors.push(err);
366                }
367                )*
368                if errors.len() != 0 {
369                    Err(::dill::ValidationError { errors })
370                } else {
371                    Ok(())
372                }
373            }
374        }
375
376        impl ::dill::TypedBuilder<#impl_type> for #builder_name {
377            fn get(&self, cat: &::dill::Catalog) -> Result<std::sync::Arc<#impl_type>, ::dill::InjectionError> {
378                use ::dill::Scope;
379
380                if let Some(inst) = self.dill_builder_scope.get() {
381                    return Ok(inst.downcast().unwrap());
382                }
383
384                let inst = ::std::sync::Arc::new(self.build(cat)?);
385
386                self.dill_builder_scope.set(inst.clone());
387                Ok(inst)
388            }
389
390            fn bind_interfaces(&self, cat: &mut ::dill::CatalogBuilder) {
391                #(
392                    cat.bind::<#interfaces, #impl_type>();
393                )*
394            }
395        }
396
397        #(
398            // Allows casting TypedBuilder<T> into TypedBuilder<dyn I> for all declared interfaces
399            impl ::dill::TypedBuilderCast<#interfaces> for #builder_name
400            {
401                fn cast(self) -> impl ::dill::TypedBuilder<#interfaces> {
402                    struct _B(#builder_name);
403
404                    impl ::dill::Builder for _B {
405                        fn instance_type_id(&self) -> ::std::any::TypeId {
406                            self.0.instance_type_id()
407                        }
408                        fn instance_type_name(&self) -> &'static str {
409                            self.0.instance_type_name()
410                        }
411                        fn interfaces(&self, clb: &mut dyn FnMut(&::dill::InterfaceDesc) -> bool) {
412                            self.0.interfaces(clb)
413                        }
414                        fn metadata<'a>(&'a self, clb: &mut dyn FnMut(&'a dyn std::any::Any) -> bool) {
415                            self.0.metadata(clb)
416                        }
417                        fn get_any(&self, cat: &::dill::Catalog) -> Result<std::sync::Arc<dyn std::any::Any + Send + Sync>, ::dill::InjectionError> {
418                            self.0.get_any(cat)
419                        }
420                        fn check(&self, cat: &::dill::Catalog) -> Result<(), ::dill::ValidationError> {
421                            self.0.check(cat)
422                        }
423                    }
424
425                    impl ::dill::TypedBuilder<#interfaces> for _B {
426                        fn get(&self, cat: &::dill::Catalog) -> Result<::std::sync::Arc<#interfaces>, ::dill::InjectionError> {
427                            match self.0.get(cat) {
428                                Ok(v) => Ok(v),
429                                Err(e) => Err(e),
430                            }
431                        }
432
433                        fn bind_interfaces(&self, cat: &mut ::dill::CatalogBuilder) {
434                            self.0.bind_interfaces(cat);
435                        }
436                    }
437
438                    _B(self)
439                }
440            }
441        )*
442    };
443
444    quote! {
445        #component_or_explicit_factory
446
447        #builder
448    }
449    .into()
450}
451
452////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
453
454fn implement_arg(
455    name: &syn::Ident,
456    typ: &syn::Type,
457    builder: &syn::Ident,
458    is_explicit: bool,
459) -> (
460    proc_macro2::TokenStream, // override_fn_field
461    proc_macro2::TokenStream, // override_fn_field_ctor
462    proc_macro2::TokenStream, // override_setters
463    proc_macro2::TokenStream, // prepare_dependency
464    proc_macro2::TokenStream, // provide_dependency
465    proc_macro2::TokenStream, // check_dependency
466) {
467    let override_fn_name = format_ident!("arg_{}_fn", name);
468
469    let injection_type = if is_explicit {
470        InjectionType::Value { typ: typ.clone() }
471    } else {
472        types::deduce_injection_type(typ)
473    };
474
475    // Used to declare the field that stores the override factory function or
476    // an explicit argument
477    let override_fn_field = if is_explicit {
478        quote! { #name: #typ }
479    } else {
480        match &injection_type {
481            InjectionType::Reference { .. } => proc_macro2::TokenStream::new(),
482            _ => quote! {
483                #override_fn_name: Option<Box<dyn Fn(&::dill::Catalog) -> Result<#typ, ::dill::InjectionError> + Send + Sync>>
484            },
485        }
486    };
487
488    // Used initialize the field that stores the override factory function or
489    // an explicit argument
490    let override_fn_field_ctor = if is_explicit {
491        quote! { #name: #name }
492    } else {
493        match &injection_type {
494            InjectionType::Reference { .. } => proc_macro2::TokenStream::new(),
495            _ => quote! { #override_fn_name: None },
496        }
497    };
498
499    // Used to create with_* and with_*_fn setters for dependency overrides
500    let override_setters = if is_explicit {
501        proc_macro2::TokenStream::new()
502    } else {
503        match &injection_type {
504            InjectionType::Reference { .. } => proc_macro2::TokenStream::new(),
505            _ => {
506                let setter_val_name = format_ident!("with_{}", name);
507                let setter_fn_name = format_ident!("with_{}_fn", name);
508                quote! {
509                    pub fn #setter_val_name(mut self, val: #typ) -> #builder {
510                        self.#override_fn_name = Some(Box::new(move |_| Ok(val.clone())));
511                        self
512                    }
513
514                    pub fn #setter_fn_name(
515                        mut self,
516                        fun: impl Fn(&::dill::Catalog) -> Result<#typ, ::dill::InjectionError> + 'static + Send + Sync
517                    ) -> #builder {
518                        self.#override_fn_name = Some(Box::new(fun));
519                        self
520                    }
521                }
522            }
523        }
524    };
525
526    // Used in TBuilder::check() to validate the dependency
527    let check_dependency = if is_explicit {
528        quote! { Ok(()) }
529    } else {
530        let do_check_dependency = get_do_check_dependency(&injection_type);
531        match &injection_type {
532            InjectionType::Reference { .. } => quote! { #do_check_dependency },
533            _ => quote! {
534                match &self.#override_fn_name {
535                    Some(_) => Ok(()),
536                    _ => #do_check_dependency,
537                }
538            },
539        }
540    };
541
542    // Used in TBuilder::build() to extract the dependency from the catalog
543    let prepare_dependency = if is_explicit {
544        proc_macro2::TokenStream::new()
545    } else {
546        let do_get_dependency = get_do_get_dependency(&injection_type);
547        match &injection_type {
548            InjectionType::Reference { .. } => quote! { let #name = #do_get_dependency; },
549            _ => quote! {
550                let #name = match &self.#override_fn_name {
551                    Some(fun) => fun(cat)?,
552                    _ => #do_get_dependency,
553                };
554            },
555        }
556    };
557
558    // Called to provide dependency value to T's constructor
559    let provide_dependency = if is_explicit {
560        quote! { self.#name.clone() }
561    } else {
562        match &injection_type {
563            InjectionType::Reference { .. } => quote! { #name.as_ref() },
564            _ => quote! { #name },
565        }
566    };
567
568    (
569        override_fn_field,
570        override_fn_field_ctor,
571        override_setters,
572        prepare_dependency,
573        provide_dependency,
574        check_dependency,
575    )
576}
577
578////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
579
580fn get_do_check_dependency(injection_type: &InjectionType) -> proc_macro2::TokenStream {
581    match injection_type {
582        InjectionType::Arc { inner } => quote! { ::dill::OneOf::<#inner>::check(cat) },
583        InjectionType::Reference { inner } => quote! { ::dill::OneOf::<#inner>::check(cat) },
584        InjectionType::Option { element } => match element.as_ref() {
585            InjectionType::Arc { inner } => {
586                quote! { ::dill::Maybe::<::dill::OneOf::<#inner>>::check(cat) }
587            }
588            InjectionType::Value { typ } => {
589                quote! { ::dill::Maybe::<::dill::OneOf::<#typ>>::check(cat) }
590            }
591            _ => {
592                unimplemented!("Currently only Option<Arc<Iface>> and Option<Value> are supported")
593            }
594        },
595        InjectionType::Lazy { element } => match element.as_ref() {
596            InjectionType::Arc { inner } => {
597                quote! { ::dill::specs::Lazy::<::dill::OneOf::<#inner>>::check(cat) }
598            }
599            _ => unimplemented!("Currently only Lazy<Arc<Iface>> is supported"),
600        },
601        InjectionType::Vec { item } => match item.as_ref() {
602            InjectionType::Arc { inner } => quote! { ::dill::AllOf::<#inner>::check(cat) },
603            _ => unimplemented!("Currently only Vec<Arc<Iface>> is supported"),
604        },
605        InjectionType::Value { typ } => quote! { ::dill::OneOf::<#typ>::check(cat) },
606    }
607}
608
609fn get_do_get_dependency(injection_type: &InjectionType) -> proc_macro2::TokenStream {
610    match injection_type {
611        InjectionType::Arc { inner } => quote! { ::dill::OneOf::<#inner>::get(cat)? },
612        InjectionType::Reference { inner } => quote! { ::dill::OneOf::<#inner>::get(cat)? },
613        InjectionType::Option { element } => match element.as_ref() {
614            InjectionType::Arc { inner } => {
615                quote! { ::dill::Maybe::<::dill::OneOf::<#inner>>::get(cat)? }
616            }
617            InjectionType::Value { typ } => {
618                quote! { ::dill::Maybe::<::dill::OneOf::<#typ>>::get(cat)?.map(|v| v.as_ref().clone()) }
619            }
620            _ => {
621                unimplemented!("Currently only Option<Arc<Iface>> and Option<Value> are supported")
622            }
623        },
624        InjectionType::Lazy { element } => match element.as_ref() {
625            InjectionType::Arc { inner } => {
626                quote! { ::dill::specs::Lazy::<::dill::OneOf::<#inner>>::get(cat)? }
627            }
628            _ => unimplemented!("Currently only Lazy<Arc<Iface>> is supported"),
629        },
630        InjectionType::Vec { item } => match item.as_ref() {
631            InjectionType::Arc { inner } => quote! { ::dill::AllOf::<#inner>::get(cat)? },
632            _ => unimplemented!("Currently only Vec<Arc<Iface>> is supported"),
633        },
634        InjectionType::Value { typ } => {
635            quote! { ::dill::OneOf::<#typ>::get(cat).map(|v| v.as_ref().clone())? }
636        }
637    }
638}
639
640////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
641
642fn implement_meta_var(index: usize, expr: &syn::ExprStruct) -> proc_macro2::TokenStream {
643    let ident = format_ident!("_meta_{index}");
644    let typ = &expr.path;
645    quote! {
646        const #ident: #typ = #expr;
647    }
648}
649
650fn implement_meta_provide(index: usize, _expr: &syn::ExprStruct) -> proc_macro2::TokenStream {
651    let ident = format_ident!("_meta_{index}");
652    quote! {
653        if !clb(&Self::#ident) { return }
654    }
655}
656
657////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
658
659/// Searches for `#[scope(X)]` attribute and returns `X`
660fn get_scope(attrs: &Vec<syn::Attribute>) -> Option<syn::Path> {
661    let mut scope = None;
662
663    for attr in attrs {
664        if is_dill_attr(attr, "scope") {
665            attr.parse_nested_meta(|meta| {
666                scope = Some(meta.path);
667                Ok(())
668            })
669            .unwrap();
670        }
671    }
672
673    scope
674}
675
676////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
677
678/// Searches for all `#[interface(X)]` attributes and returns all types
679fn get_interfaces(attrs: &Vec<syn::Attribute>) -> Vec<syn::Type> {
680    let mut interfaces = Vec::new();
681
682    for attr in attrs {
683        if is_dill_attr(attr, "interface") {
684            let iface = attr.parse_args().unwrap();
685            interfaces.push(iface);
686        }
687    }
688
689    interfaces
690}
691
692////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
693
694/// Searches for all `#[meta(X)]` attributes and returns all expressions
695fn get_meta(attrs: &Vec<syn::Attribute>) -> Vec<syn::ExprStruct> {
696    let mut meta = Vec::new();
697
698    for attr in attrs {
699        if is_dill_attr(attr, "meta") {
700            let expr = attr.parse_args().unwrap();
701            meta.push(expr);
702        }
703    }
704
705    meta
706}
707
708////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
709
710fn is_dill_attr<I: ?Sized>(attr: &syn::Attribute, ident: &I) -> bool
711where
712    syn::Ident: PartialEq<I>,
713{
714    if attr.path().is_ident(ident) {
715        true
716    } else {
717        attr.path().segments.len() == 2
718            && &attr.path().segments[0].ident == "dill"
719            && attr.path().segments[1].ident == *ident
720    }
721}
722
723////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
724
725/// Searches `impl` block for `new()` method
726fn get_new(impl_items: &mut [syn::ImplItem]) -> Option<&mut syn::ImplItemFn> {
727    impl_items
728        .iter_mut()
729        .filter_map(|i| match i {
730            syn::ImplItem::Fn(m) => Some(m),
731            _ => None,
732        })
733        .find(|m| m.sig.ident == "new")
734}
735
736////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
737
738fn extract_attr_explicit(attrs: &mut Vec<syn::Attribute>) -> bool {
739    let mut present = false;
740    attrs.retain_mut(|attr| {
741        if is_attr_explicit(attr) {
742            present = true;
743            false
744        } else {
745            true
746        }
747    });
748    present
749}
750
751fn is_attr_explicit(attr: &syn::Attribute) -> bool {
752    if !is_dill_attr(attr, "component") {
753        return false;
754    }
755    let syn::Meta::List(meta) = &attr.meta else {
756        return false;
757    };
758    meta.tokens.to_string().contains("explicit")
759}