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