Skip to main content

auto_di_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{
4    FnArg, GenericArgument, ImplItem, ImplItemFn, Item, ItemFn, ItemImpl, ItemStruct, LitStr, Pat,
5    PathArguments, ReturnType, Signature, Type, parse_macro_input,
6};
7
8#[derive(Default, Clone)]
9struct ProviderOptions {
10    name: Option<String>,
11    primary: bool,
12    scope: Option<String>,
13    eager: bool,
14    profile: Option<String>,
15    condition_key: Option<String>,
16    condition_value: Option<String>,
17    post_construct: Option<String>,
18    pre_destroy: Option<String>,
19}
20
21/// Registers a lazily-created singleton provider.
22///
23/// It can be placed on either a free provider function or an inherent `impl`
24/// containing a `new` constructor. Constructors may be synchronous or async.
25/// Every injected parameter must have the form `name: Arc<Dependency>`.
26#[proc_macro_attribute]
27pub fn singleton(attribute: TokenStream, item: TokenStream) -> TokenStream {
28    let options = match parse_provider_options(attribute) {
29        Ok(options) => options,
30        Err(error) => return error.to_compile_error().into(),
31    };
32    let item = parse_macro_input!(item as Item);
33
34    let expanded = match item {
35        Item::Fn(function) => expand_function(function, &options),
36        Item::Impl(item_impl) => expand_impl(item_impl, &options),
37        other => Err(syn::Error::new_spanned(
38            other,
39            "#[singleton] can only be used on a function or an inherent impl block",
40        )),
41    };
42
43    match expanded {
44        Ok(tokens) => tokens.into(),
45        Err(error) => error.to_compile_error().into(),
46    }
47}
48
49fn parse_provider_options(attribute: TokenStream) -> syn::Result<ProviderOptions> {
50    parse_provider_options2(attribute.into())
51}
52
53fn parse_provider_options2(attribute: proc_macro2::TokenStream) -> syn::Result<ProviderOptions> {
54    use syn::parse::Parser;
55    let mut options = ProviderOptions::default();
56    let parser = syn::meta::parser(|meta| {
57        if meta.path.is_ident("primary") {
58            options.primary = true;
59            return Ok(());
60        }
61        if meta.path.is_ident("eager") {
62            options.eager = true;
63            return Ok(());
64        }
65        let value = meta.value()?.parse::<LitStr>()?.value();
66        if meta.path.is_ident("name") {
67            options.name = Some(value);
68        } else if meta.path.is_ident("scope") {
69            options.scope = Some(value);
70        } else if meta.path.is_ident("profile") {
71            options.profile = Some(value);
72        } else if meta.path.is_ident("condition") {
73            let (key, expected) = value
74                .split_once('=')
75                .map_or((value.as_str(), None), |(k, v)| (k, Some(v)));
76            options.condition_key = Some(key.to_owned());
77            options.condition_value = expected.map(str::to_owned);
78        } else if meta.path.is_ident("post_construct") {
79            options.post_construct = Some(value);
80        } else if meta.path.is_ident("pre_destroy") {
81            options.pre_destroy = Some(value);
82        } else {
83            return Err(meta.error("unknown provider option"));
84        }
85        Ok(())
86    });
87    parser.parse2(attribute)?;
88    if let Some(scope) = &options.scope {
89        if !matches!(scope.as_str(), "singleton" | "prototype" | "request") {
90            return Err(syn::Error::new(
91                proc_macro2::Span::call_site(),
92                "scope must be singleton, prototype, or request",
93            ));
94        }
95    }
96    Ok(options)
97}
98
99#[proc_macro_attribute]
100pub fn component(attribute: TokenStream, item: TokenStream) -> TokenStream {
101    singleton(attribute, item)
102}
103
104#[proc_macro_attribute]
105pub fn service(attribute: TokenStream, item: TokenStream) -> TokenStream {
106    singleton(attribute, item)
107}
108
109#[proc_macro_attribute]
110pub fn repository(attribute: TokenStream, item: TokenStream) -> TokenStream {
111    singleton(attribute, item)
112}
113
114/// Marker consumed from constructor parameters by the surrounding provider macro.
115#[proc_macro_attribute]
116pub fn qualifier(_attribute: TokenStream, item: TokenStream) -> TokenStream {
117    item
118}
119
120/// Boots the global context, creates eager beans, and runs shutdown hooks.
121#[proc_macro_attribute]
122pub fn application(_attribute: TokenStream, item: TokenStream) -> TokenStream {
123    let mut function = parse_macro_input!(item as ItemFn);
124    if function.sig.asyncness.is_none() {
125        return syn::Error::new_spanned(&function.sig, "#[application] requires async fn")
126            .to_compile_error()
127            .into();
128    }
129    let body = function.block;
130    function
131        .attrs
132        .push(syn::parse_quote!(#[::auto_di::__private::tokio::main]));
133    function.block = Box::new(syn::parse_quote!({
134        let __container = ::auto_di::global_container()?;
135        __container.initialize_eager().await?;
136        let __result = (async move #body).await;
137        __container.shutdown().await?;
138        __result
139    }));
140    quote!(#function).into()
141}
142
143/// Generates environment binding and registers the resulting struct as a bean.
144#[proc_macro_attribute]
145pub fn configuration_properties(attribute: TokenStream, item: TokenStream) -> TokenStream {
146    let prefix = parse_macro_input!(attribute as LitStr).value();
147    let structure = parse_macro_input!(item as ItemStruct);
148    match expand_configuration_properties(prefix, structure) {
149        Ok(tokens) => tokens.into(),
150        Err(error) => error.to_compile_error().into(),
151    }
152}
153
154fn expand_configuration_properties(
155    prefix: String,
156    structure: ItemStruct,
157) -> syn::Result<proc_macro2::TokenStream> {
158    let ident = &structure.ident;
159    let fields = match &structure.fields {
160        syn::Fields::Named(fields) => &fields.named,
161        _ => {
162            return Err(syn::Error::new_spanned(
163                &structure,
164                "configuration properties require named fields",
165            ));
166        }
167    };
168    let bindings = fields.iter().map(|field| {
169        let name = field.ident.as_ref().expect("named field");
170        let ty = &field.ty;
171        let key = format!("{}_{}", prefix, name).replace(['.', '-'], "_").to_uppercase();
172        quote! {
173            #name: ::std::env::var(#key)
174                .map_err(|error| ::auto_di::DiError::Configuration { key: #key.into(), message: error.to_string() })?
175                .parse::<#ty>()
176                .map_err(|_| ::auto_di::DiError::Configuration { key: #key.into(), message: "value could not be parsed".into() })?
177        }
178    });
179    let provider_name = format_ident!("configuration_properties_{ident}");
180    let invocation = quote! { <#ident as ::auto_di::ConfigurationProperties>::from_environment()? };
181    let registration = registration(
182        &provider_name,
183        &syn::parse_quote!(#ident),
184        &[],
185        &[],
186        &[],
187        invocation,
188    );
189    Ok(quote! {
190        #structure
191        impl ::auto_di::ConfigurationProperties for #ident {
192            fn from_environment() -> ::std::result::Result<Self, ::auto_di::DiError> {
193                Ok(Self { #(#bindings),* })
194            }
195        }
196        #registration
197    })
198}
199
200/// Registers all `#[bean]` methods in an inherent impl as singleton providers.
201/// The configuration object is itself a singleton created through `Default`.
202#[proc_macro_attribute]
203pub fn configuration(_attribute: TokenStream, item: TokenStream) -> TokenStream {
204    let item_impl = parse_macro_input!(item as ItemImpl);
205    match expand_configuration(item_impl) {
206        Ok(tokens) => tokens.into(),
207        Err(error) => error.to_compile_error().into(),
208    }
209}
210
211/// Marker attribute consumed by `#[configuration]`.
212#[proc_macro_attribute]
213pub fn bean(_attribute: TokenStream, item: TokenStream) -> TokenStream {
214    item
215}
216
217fn expand_function(
218    mut function: ItemFn,
219    options: &ProviderOptions,
220) -> syn::Result<proc_macro2::TokenStream> {
221    let function_name = function.sig.ident.clone();
222    let output_type = match &function.sig.output {
223        ReturnType::Type(_, ty) => ty.as_ref().clone(),
224        ReturnType::Default => {
225            return Err(syn::Error::new_spanned(
226                &function.sig,
227                "a #[singleton] provider must return a value",
228            ));
229        }
230    };
231    let (argument_names, dependency_types, qualifiers) = dependencies(&mut function.sig)?;
232    let invocation = if function.sig.asyncness.is_some() {
233        quote! { #function_name(#(#argument_names),*).await }
234    } else {
235        quote! { #function_name(#(#argument_names),*) }
236    };
237
238    let registration = registration_with_options(
239        &function_name,
240        &output_type,
241        &argument_names,
242        &dependency_types,
243        &qualifiers,
244        invocation,
245        options,
246    );
247
248    Ok(quote! {
249        #function
250        #registration
251    })
252}
253
254fn expand_impl(
255    mut item_impl: ItemImpl,
256    options: &ProviderOptions,
257) -> syn::Result<proc_macro2::TokenStream> {
258    if item_impl.trait_.is_some() {
259        return Err(syn::Error::new_spanned(
260            &item_impl,
261            "#[singleton] requires an inherent impl, not a trait impl",
262        ));
263    }
264    if !item_impl.generics.params.is_empty() {
265        return Err(syn::Error::new_spanned(
266            &item_impl.generics,
267            "generic singleton impl blocks are not supported yet",
268        ));
269    }
270
271    let constructor_index = item_impl
272        .items
273        .iter()
274        .position(|item| match item {
275            ImplItem::Fn(function) => function.sig.ident == "new",
276            _ => false,
277        })
278        .ok_or_else(|| {
279            syn::Error::new_spanned(
280                &item_impl.self_ty,
281                "a #[singleton] impl must contain a new(...) -> Self constructor",
282            )
283        })?;
284    let output_type = item_impl.self_ty.as_ref().clone();
285    let constructor = match &mut item_impl.items[constructor_index] {
286        ImplItem::Fn(function) => function,
287        _ => unreachable!(),
288    };
289    validate_impl_constructor(constructor)?;
290
291    let (argument_names, dependency_types, qualifiers) = dependencies(&mut constructor.sig)?;
292    let type_ident = singleton_type_ident(&output_type)?;
293    let constructor_name = constructor.sig.ident.clone();
294    let invocation = if constructor.sig.asyncness.is_some() {
295        quote! { <#output_type>::#constructor_name(#(#argument_names),*).await }
296    } else {
297        quote! { <#output_type>::#constructor_name(#(#argument_names),*) }
298    };
299    let registration = registration_with_options(
300        type_ident,
301        &output_type,
302        &argument_names,
303        &dependency_types,
304        &qualifiers,
305        invocation,
306        options,
307    );
308
309    Ok(quote! {
310        #item_impl
311        #registration
312    })
313}
314
315fn expand_configuration(mut item_impl: ItemImpl) -> syn::Result<proc_macro2::TokenStream> {
316    if item_impl.trait_.is_some() || !item_impl.generics.params.is_empty() {
317        return Err(syn::Error::new_spanned(
318            &item_impl,
319            "#[configuration] requires a non-generic inherent impl",
320        ));
321    }
322
323    let configuration_type = item_impl.self_ty.as_ref().clone();
324    let configuration_ident = singleton_type_ident(&configuration_type)?.clone();
325    let configuration_registration = registration(
326        &format_ident!("configuration_{configuration_ident}"),
327        &configuration_type,
328        &[],
329        &[],
330        &[],
331        quote! { <#configuration_type as ::std::default::Default>::default() },
332    );
333    let mut registrations = Vec::new();
334
335    for item in &mut item_impl.items {
336        let ImplItem::Fn(method) = item else { continue };
337        let Some(bean_attribute) = method
338            .attrs
339            .iter()
340            .find(|attr| attr.path().is_ident("bean"))
341        else {
342            continue;
343        };
344        let bean_options = match &bean_attribute.meta {
345            syn::Meta::Path(_) => ProviderOptions::default(),
346            syn::Meta::List(list) => parse_provider_options2(list.tokens.clone())?,
347            syn::Meta::NameValue(_) => {
348                return Err(syn::Error::new_spanned(bean_attribute, "use #[bean(...)]"));
349            }
350        };
351        method.attrs.retain(|attr| !attr.path().is_ident("bean"));
352
353        let output_type = match &method.sig.output {
354            ReturnType::Type(_, ty) => ty.as_ref().clone(),
355            ReturnType::Default => {
356                return Err(syn::Error::new_spanned(
357                    &method.sig,
358                    "a #[bean] method must return a value",
359                ));
360            }
361        };
362        let (has_receiver, argument_names, dependency_types, qualifiers) =
363            bean_dependencies(&mut method.sig)?;
364        let method_name = method.sig.ident.clone();
365        let registration_name = format_ident!("bean_{configuration_ident}_{method_name}");
366        let invocation = if has_receiver {
367            quote! { configuration.#method_name(#(#argument_names),*) }
368        } else {
369            quote! { <#configuration_type>::#method_name(#(#argument_names),*) }
370        };
371        let invocation = if method.sig.asyncness.is_some() {
372            quote! { #invocation.await }
373        } else {
374            invocation
375        };
376        let prelude = has_receiver.then(|| {
377            quote! {
378                let configuration: ::std::sync::Arc<#configuration_type> =
379                    container.resolve_dependency::<#configuration_type>(&context).await?;
380            }
381        });
382
383        registrations.push(registration_with_prelude(
384            &registration_name,
385            &output_type,
386            &argument_names,
387            &dependency_types,
388            &qualifiers,
389            prelude.unwrap_or_default(),
390            invocation,
391            &bean_options,
392        ));
393    }
394
395    if registrations.is_empty() {
396        return Err(syn::Error::new_spanned(
397            &item_impl,
398            "#[configuration] must contain at least one #[bean] method",
399        ));
400    }
401
402    Ok(quote! {
403        #item_impl
404        #configuration_registration
405        #(#registrations)*
406    })
407}
408
409fn validate_impl_constructor(constructor: &ImplItemFn) -> syn::Result<()> {
410    if constructor.sig.receiver().is_some() {
411        return Err(syn::Error::new_spanned(
412            &constructor.sig,
413            "the singleton new constructor must be an associated function",
414        ));
415    }
416    match &constructor.sig.output {
417        ReturnType::Type(_, ty) if matches!(ty.as_ref(), Type::Path(path) if path.path.is_ident("Self")) => {
418            Ok(())
419        }
420        _ => Err(syn::Error::new_spanned(
421            &constructor.sig.output,
422            "the singleton new constructor must return Self",
423        )),
424    }
425}
426
427fn dependencies(
428    signature: &mut Signature,
429) -> syn::Result<(Vec<syn::Ident>, Vec<Type>, Vec<Option<String>>)> {
430    let mut argument_names = Vec::new();
431    let mut dependency_types = Vec::new();
432    let mut qualifiers = Vec::new();
433
434    for input in &mut signature.inputs {
435        let FnArg::Typed(argument) = input else {
436            return Err(syn::Error::new_spanned(
437                input,
438                "singleton constructors cannot have a self receiver",
439            ));
440        };
441        let Pat::Ident(pattern) = argument.pat.as_ref() else {
442            return Err(syn::Error::new_spanned(
443                &argument.pat,
444                "use a simple parameter name for an injected dependency",
445            ));
446        };
447        argument_names.push(pattern.ident.clone());
448        validate_dependency_type(argument.ty.as_ref())?;
449        dependency_types.push(argument.ty.as_ref().clone());
450        qualifiers.push(take_qualifier(&mut argument.attrs)?);
451    }
452    Ok((argument_names, dependency_types, qualifiers))
453}
454
455fn bean_dependencies(
456    signature: &mut Signature,
457) -> syn::Result<(bool, Vec<syn::Ident>, Vec<Type>, Vec<Option<String>>)> {
458    let mut has_receiver = false;
459    let mut names = Vec::new();
460    let mut types = Vec::new();
461    let mut qualifiers = Vec::new();
462    for input in &mut signature.inputs {
463        match input {
464            FnArg::Receiver(receiver) => {
465                if has_receiver || receiver.reference.is_none() || receiver.mutability.is_some() {
466                    return Err(syn::Error::new_spanned(
467                        receiver,
468                        "a #[bean] method only supports an immutable &self receiver",
469                    ));
470                }
471                has_receiver = true;
472            }
473            FnArg::Typed(argument) => {
474                let Pat::Ident(pattern) = argument.pat.as_ref() else {
475                    return Err(syn::Error::new_spanned(
476                        &argument.pat,
477                        "use a simple parameter name for an injected dependency",
478                    ));
479                };
480                names.push(pattern.ident.clone());
481                validate_dependency_type(argument.ty.as_ref())?;
482                types.push(argument.ty.as_ref().clone());
483                qualifiers.push(take_qualifier(&mut argument.attrs)?);
484            }
485        }
486    }
487    Ok((has_receiver, names, types, qualifiers))
488}
489
490fn take_qualifier(attributes: &mut Vec<syn::Attribute>) -> syn::Result<Option<String>> {
491    let mut value = None;
492    for attribute in attributes
493        .iter()
494        .filter(|attr| attr.path().is_ident("qualifier"))
495    {
496        if value.is_some() {
497            return Err(syn::Error::new_spanned(
498                attribute,
499                "only one qualifier is allowed",
500            ));
501        }
502        value = Some(attribute.parse_args::<LitStr>()?.value());
503    }
504    attributes.retain(|attr| !attr.path().is_ident("qualifier"));
505    Ok(value)
506}
507
508fn registration(
509    name: &syn::Ident,
510    output_type: &Type,
511    argument_names: &[syn::Ident],
512    dependency_types: &[Type],
513    qualifiers: &[Option<String>],
514    invocation: proc_macro2::TokenStream,
515) -> proc_macro2::TokenStream {
516    registration_with_options(
517        name,
518        output_type,
519        argument_names,
520        dependency_types,
521        qualifiers,
522        invocation,
523        &ProviderOptions::default(),
524    )
525}
526
527fn registration_with_options(
528    name: &syn::Ident,
529    output_type: &Type,
530    argument_names: &[syn::Ident],
531    dependency_types: &[Type],
532    qualifiers: &[Option<String>],
533    invocation: proc_macro2::TokenStream,
534    options: &ProviderOptions,
535) -> proc_macro2::TokenStream {
536    registration_with_prelude(
537        name,
538        output_type,
539        argument_names,
540        dependency_types,
541        qualifiers,
542        quote! {},
543        invocation,
544        options,
545    )
546}
547
548fn registration_with_prelude(
549    name: &syn::Ident,
550    output_type: &Type,
551    argument_names: &[syn::Ident],
552    dependency_types: &[Type],
553    qualifiers: &[Option<String>],
554    prelude: proc_macro2::TokenStream,
555    invocation: proc_macro2::TokenStream,
556    options: &ProviderOptions,
557) -> proc_macro2::TokenStream {
558    let factory_name = format_ident!("__di_factory_{name}");
559    let type_id_name = format_ident!("__di_type_id_{name}");
560    let type_name_name = format_ident!("__di_type_name_{name}");
561    let destroy_name = format_ident!("__di_destroy_{name}");
562    let option_tokens = |value: Option<&str>| match value {
563        Some(value) => quote!(Some(#value)),
564        None => quote!(None),
565    };
566    let bean_name = option_tokens(options.name.as_deref());
567    let primary = options.primary;
568    let eager = options.eager;
569    let profile = option_tokens(options.profile.as_deref());
570    let condition_key = option_tokens(options.condition_key.as_deref());
571    let condition_value = option_tokens(options.condition_value.as_deref());
572    let scope = match options.scope.as_deref().unwrap_or("singleton") {
573        "prototype" => quote!(::auto_di::Scope::Prototype),
574        "request" => quote!(::auto_di::Scope::Request),
575        _ => quote!(::auto_di::Scope::Singleton),
576    };
577    let post_construct = options.post_construct.as_ref().map(|method| {
578        let method = format_ident!("{method}");
579        quote! { value.#method().await; }
580    });
581    let (destroy_function, destroy_value) = if let Some(method) = &options.pre_destroy {
582        let method = format_ident!("{method}");
583        (
584            quote! {
585                #[doc(hidden)]
586                fn #destroy_name(value: ::auto_di::DynArc) -> ::auto_di::BoxFuture<'static, ::std::result::Result<(), ::auto_di::DiError>> {
587                    ::std::boxed::Box::pin(async move {
588                        let value = value.downcast::<#output_type>()
589                            .map_err(|_| ::auto_di::DiError::TypeMismatch(::std::any::type_name::<#output_type>()))?;
590                        value.#method().await;
591                        Ok(())
592                    })
593                }
594            },
595            quote!(Some(#destroy_name as _)),
596        )
597    } else {
598        (quote! {}, quote!(None))
599    };
600    let resolve_dependencies = argument_names
601        .iter()
602        .zip(dependency_types.iter())
603        .zip(qualifiers.iter())
604        .map(|((name, ty), qualifier)| dependency_resolution(name, ty, qualifier.as_deref()));
605
606    quote! {
607        #[doc(hidden)]
608        fn #type_id_name() -> ::std::any::TypeId {
609            ::std::any::TypeId::of::<#output_type>()
610        }
611
612        #[doc(hidden)]
613        fn #type_name_name() -> &'static str {
614            ::std::any::type_name::<#output_type>()
615        }
616
617        #[doc(hidden)]
618        fn #factory_name<'a>(
619            container: &'a ::auto_di::Container,
620            context: ::auto_di::ResolutionContext,
621        ) -> ::auto_di::BoxFuture<'a, ::std::result::Result<::auto_di::DynArc, ::auto_di::DiError>> {
622            ::std::boxed::Box::pin(async move {
623                #prelude
624                #(#resolve_dependencies)*
625                let value: #output_type = #invocation;
626                #post_construct
627                Ok(::std::sync::Arc::new(value) as ::auto_di::DynArc)
628            })
629        }
630
631        #destroy_function
632
633        ::auto_di::__private::inventory::submit! {
634            ::auto_di::ProviderDescriptor::configured(
635                #type_id_name,
636                #type_name_name,
637                #factory_name,
638                #bean_name,
639                #primary,
640                #scope,
641                #eager,
642                #profile,
643                #condition_key,
644                #condition_value,
645                #destroy_value,
646            )
647        }
648    }
649}
650
651fn singleton_type_ident(ty: &Type) -> syn::Result<&syn::Ident> {
652    let Type::Path(path) = ty else {
653        return Err(syn::Error::new_spanned(
654            ty,
655            "singleton impl type must be a named type",
656        ));
657    };
658    path.path
659        .segments
660        .last()
661        .map(|segment| &segment.ident)
662        .ok_or_else(|| syn::Error::new_spanned(ty, "singleton impl type must be a named type"))
663}
664
665fn validate_dependency_type(ty: &Type) -> syn::Result<()> {
666    if generic_inner(ty, "Arc").is_some()
667        || generic_inner(ty, "Provider").is_some()
668        || generic_inner(ty, "Lazy").is_some()
669    {
670        return Ok(());
671    }
672    if let Some(inner) = generic_inner(ty, "Option").or_else(|| generic_inner(ty, "Vec")) {
673        if generic_inner(inner, "Arc").is_some() {
674            return Ok(());
675        }
676    }
677    Err(syn::Error::new_spanned(
678        ty,
679        "dependency must be Arc<T>, Option<Arc<T>>, Vec<Arc<T>>, Provider<T>, or Lazy<T>",
680    ))
681}
682
683fn generic_inner<'a>(ty: &'a Type, expected: &str) -> Option<&'a Type> {
684    let Type::Path(path) = ty else { return None };
685    let segment = path.path.segments.last()?;
686    if segment.ident != expected {
687        return None;
688    }
689    let PathArguments::AngleBracketed(arguments) = &segment.arguments else {
690        return None;
691    };
692    match arguments.args.first() {
693        Some(GenericArgument::Type(inner)) if arguments.args.len() == 1 => Some(inner),
694        _ => None,
695    }
696}
697
698fn dependency_resolution(
699    name: &syn::Ident,
700    ty: &Type,
701    qualifier: Option<&str>,
702) -> proc_macro2::TokenStream {
703    if let Some(inner) = generic_inner(ty, "Arc") {
704        if matches!(inner, Type::TraitObject(_)) {
705            if let Some(qualifier) = qualifier {
706                return quote! {
707                    let #name: #ty = (*container.resolve_named_dependency::<#ty>(#qualifier, &context).await?).clone();
708                };
709            }
710            return quote! {
711                let #name: #ty = (*container.resolve_dependency::<#ty>(&context).await?).clone();
712            };
713        }
714        if let Some(qualifier) = qualifier {
715            return quote! {
716                let #name: #ty = container.resolve_named_dependency::<#inner>(#qualifier, &context).await?;
717            };
718        }
719        return quote! {
720            let #name: #ty = container.resolve_dependency::<#inner>(&context).await?;
721        };
722    }
723    if let Some(wrapped) = generic_inner(ty, "Option") {
724        let inner = generic_inner(wrapped, "Arc").expect("validated Option<Arc<T>>");
725        return quote! {
726            let #name: #ty = container.resolve_optional_dependency::<#inner>(&context).await?;
727        };
728    }
729    if let Some(wrapped) = generic_inner(ty, "Vec") {
730        let inner = generic_inner(wrapped, "Arc").expect("validated Vec<Arc<T>>");
731        return quote! {
732            let #name: #ty = container.resolve_all_dependency::<#inner>(&context).await?;
733        };
734    }
735    quote! { let #name: #ty = ::std::default::Default::default(); }
736}