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#[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#[proc_macro_attribute]
116pub fn qualifier(_attribute: TokenStream, item: TokenStream) -> TokenStream {
117 item
118}
119
120#[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#[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#[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#[proc_macro_attribute]
214pub fn beans(attribute: TokenStream, item: TokenStream) -> TokenStream {
215 configuration(attribute, item)
216}
217
218#[proc_macro_attribute]
221pub fn bean(attribute: TokenStream, item: TokenStream) -> TokenStream {
222 singleton(attribute, item)
223}
224
225fn expand_function(
226 mut function: ItemFn,
227 options: &ProviderOptions,
228) -> syn::Result<proc_macro2::TokenStream> {
229 let function_name = function.sig.ident.clone();
230 let output_type = match &function.sig.output {
231 ReturnType::Type(_, ty) => ty.as_ref().clone(),
232 ReturnType::Default => {
233 return Err(syn::Error::new_spanned(
234 &function.sig,
235 "a #[singleton] provider must return a value",
236 ));
237 }
238 };
239 let (argument_names, dependency_types, qualifiers) = dependencies(&mut function.sig)?;
240 let invocation = if function.sig.asyncness.is_some() {
241 quote! { #function_name(#(#argument_names),*).await }
242 } else {
243 quote! { #function_name(#(#argument_names),*) }
244 };
245
246 let registration = registration_with_options(
247 &function_name,
248 &output_type,
249 &argument_names,
250 &dependency_types,
251 &qualifiers,
252 invocation,
253 options,
254 );
255
256 Ok(quote! {
257 #function
258 #registration
259 })
260}
261
262fn expand_impl(
263 mut item_impl: ItemImpl,
264 options: &ProviderOptions,
265) -> syn::Result<proc_macro2::TokenStream> {
266 if item_impl.trait_.is_some() {
267 return Err(syn::Error::new_spanned(
268 &item_impl,
269 "#[singleton] requires an inherent impl, not a trait impl",
270 ));
271 }
272 if !item_impl.generics.params.is_empty() {
273 return Err(syn::Error::new_spanned(
274 &item_impl.generics,
275 "generic singleton impl blocks are not supported yet",
276 ));
277 }
278
279 let constructor_index = item_impl
280 .items
281 .iter()
282 .position(|item| match item {
283 ImplItem::Fn(function) => function.sig.ident == "new",
284 _ => false,
285 })
286 .ok_or_else(|| {
287 syn::Error::new_spanned(
288 &item_impl.self_ty,
289 "a #[singleton] impl must contain a new(...) -> Self constructor",
290 )
291 })?;
292 let output_type = item_impl.self_ty.as_ref().clone();
293 let constructor = match &mut item_impl.items[constructor_index] {
294 ImplItem::Fn(function) => function,
295 _ => unreachable!(),
296 };
297 validate_impl_constructor(constructor)?;
298
299 let (argument_names, dependency_types, qualifiers) = dependencies(&mut constructor.sig)?;
300 let type_ident = singleton_type_ident(&output_type)?;
301 let constructor_name = constructor.sig.ident.clone();
302 let invocation = if constructor.sig.asyncness.is_some() {
303 quote! { <#output_type>::#constructor_name(#(#argument_names),*).await }
304 } else {
305 quote! { <#output_type>::#constructor_name(#(#argument_names),*) }
306 };
307 let registration = registration_with_options(
308 type_ident,
309 &output_type,
310 &argument_names,
311 &dependency_types,
312 &qualifiers,
313 invocation,
314 options,
315 );
316
317 let mut bean_registrations = Vec::new();
318 for item in &mut item_impl.items {
319 let ImplItem::Fn(method) = item else { continue };
320 let Some(bean_attribute) = method
321 .attrs
322 .iter()
323 .find(|attribute| attribute.path().is_ident("bean"))
324 else {
325 continue;
326 };
327 let bean_options = match &bean_attribute.meta {
328 syn::Meta::Path(_) => ProviderOptions::default(),
329 syn::Meta::List(list) => parse_provider_options2(list.tokens.clone())?,
330 syn::Meta::NameValue(_) => {
331 return Err(syn::Error::new_spanned(bean_attribute, "use #[bean(...)]"));
332 }
333 };
334 method
335 .attrs
336 .retain(|attribute| !attribute.path().is_ident("bean"));
337
338 let bean_type = match &method.sig.output {
339 ReturnType::Type(_, ty) => ty.as_ref().clone(),
340 ReturnType::Default => {
341 return Err(syn::Error::new_spanned(
342 &method.sig,
343 "a #[bean] method must return a value",
344 ));
345 }
346 };
347 let (has_receiver, bean_arguments, bean_dependencies, bean_qualifiers) =
348 bean_dependencies(&mut method.sig)?;
349 let method_name = method.sig.ident.clone();
350 let bean_registration_name = format_ident!("bean_{type_ident}_{method_name}");
351 let bean_invocation = if has_receiver {
352 quote! { owner.#method_name(#(#bean_arguments),*) }
353 } else {
354 quote! { <#output_type>::#method_name(#(#bean_arguments),*) }
355 };
356 let bean_invocation = if method.sig.asyncness.is_some() {
357 quote! { #bean_invocation.await }
358 } else {
359 bean_invocation
360 };
361 let prelude = has_receiver.then(|| {
362 quote! {
363 let owner: ::std::sync::Arc<#output_type> =
364 container.resolve_dependency::<#output_type>(&context).await?;
365 }
366 });
367
368 bean_registrations.push(registration_with_prelude(
369 &bean_registration_name,
370 &bean_type,
371 &bean_arguments,
372 &bean_dependencies,
373 &bean_qualifiers,
374 prelude.unwrap_or_default(),
375 bean_invocation,
376 &bean_options,
377 ));
378 }
379
380 Ok(quote! {
381 #item_impl
382 #registration
383 #(#bean_registrations)*
384 })
385}
386
387fn expand_configuration(mut item_impl: ItemImpl) -> syn::Result<proc_macro2::TokenStream> {
388 if item_impl.trait_.is_some() || !item_impl.generics.params.is_empty() {
389 return Err(syn::Error::new_spanned(
390 &item_impl,
391 "#[configuration] requires a non-generic inherent impl",
392 ));
393 }
394
395 let configuration_type = item_impl.self_ty.as_ref().clone();
396 let configuration_ident = singleton_type_ident(&configuration_type)?.clone();
397 let configuration_registration = registration(
398 &format_ident!("configuration_{configuration_ident}"),
399 &configuration_type,
400 &[],
401 &[],
402 &[],
403 quote! { <#configuration_type as ::std::default::Default>::default() },
404 );
405 let mut registrations = Vec::new();
406
407 for item in &mut item_impl.items {
408 let ImplItem::Fn(method) = item else { continue };
409 let Some(bean_attribute) = method
410 .attrs
411 .iter()
412 .find(|attr| attr.path().is_ident("bean"))
413 else {
414 continue;
415 };
416 let bean_options = match &bean_attribute.meta {
417 syn::Meta::Path(_) => ProviderOptions::default(),
418 syn::Meta::List(list) => parse_provider_options2(list.tokens.clone())?,
419 syn::Meta::NameValue(_) => {
420 return Err(syn::Error::new_spanned(bean_attribute, "use #[bean(...)]"));
421 }
422 };
423 method.attrs.retain(|attr| !attr.path().is_ident("bean"));
424
425 let output_type = match &method.sig.output {
426 ReturnType::Type(_, ty) => ty.as_ref().clone(),
427 ReturnType::Default => {
428 return Err(syn::Error::new_spanned(
429 &method.sig,
430 "a #[bean] method must return a value",
431 ));
432 }
433 };
434 let (has_receiver, argument_names, dependency_types, qualifiers) =
435 bean_dependencies(&mut method.sig)?;
436 let method_name = method.sig.ident.clone();
437 let registration_name = format_ident!("bean_{configuration_ident}_{method_name}");
438 let invocation = if has_receiver {
439 quote! { configuration.#method_name(#(#argument_names),*) }
440 } else {
441 quote! { <#configuration_type>::#method_name(#(#argument_names),*) }
442 };
443 let invocation = if method.sig.asyncness.is_some() {
444 quote! { #invocation.await }
445 } else {
446 invocation
447 };
448 let prelude = has_receiver.then(|| {
449 quote! {
450 let configuration: ::std::sync::Arc<#configuration_type> =
451 container.resolve_dependency::<#configuration_type>(&context).await?;
452 }
453 });
454
455 registrations.push(registration_with_prelude(
456 ®istration_name,
457 &output_type,
458 &argument_names,
459 &dependency_types,
460 &qualifiers,
461 prelude.unwrap_or_default(),
462 invocation,
463 &bean_options,
464 ));
465 }
466
467 if registrations.is_empty() {
468 return Err(syn::Error::new_spanned(
469 &item_impl,
470 "#[configuration] must contain at least one #[bean] method",
471 ));
472 }
473
474 Ok(quote! {
475 #item_impl
476 #configuration_registration
477 #(#registrations)*
478 })
479}
480
481fn validate_impl_constructor(constructor: &ImplItemFn) -> syn::Result<()> {
482 if constructor.sig.receiver().is_some() {
483 return Err(syn::Error::new_spanned(
484 &constructor.sig,
485 "the singleton new constructor must be an associated function",
486 ));
487 }
488 match &constructor.sig.output {
489 ReturnType::Type(_, ty) if matches!(ty.as_ref(), Type::Path(path) if path.path.is_ident("Self")) => {
490 Ok(())
491 }
492 _ => Err(syn::Error::new_spanned(
493 &constructor.sig.output,
494 "the singleton new constructor must return Self",
495 )),
496 }
497}
498
499fn dependencies(
500 signature: &mut Signature,
501) -> syn::Result<(Vec<syn::Ident>, Vec<Type>, Vec<Option<String>>)> {
502 let mut argument_names = Vec::new();
503 let mut dependency_types = Vec::new();
504 let mut qualifiers = Vec::new();
505
506 for input in &mut signature.inputs {
507 let FnArg::Typed(argument) = input else {
508 return Err(syn::Error::new_spanned(
509 input,
510 "singleton constructors cannot have a self receiver",
511 ));
512 };
513 let Pat::Ident(pattern) = argument.pat.as_ref() else {
514 return Err(syn::Error::new_spanned(
515 &argument.pat,
516 "use a simple parameter name for an injected dependency",
517 ));
518 };
519 argument_names.push(pattern.ident.clone());
520 validate_dependency_type(argument.ty.as_ref())?;
521 dependency_types.push(argument.ty.as_ref().clone());
522 qualifiers.push(take_qualifier(&mut argument.attrs)?);
523 }
524 Ok((argument_names, dependency_types, qualifiers))
525}
526
527fn bean_dependencies(
528 signature: &mut Signature,
529) -> syn::Result<(bool, Vec<syn::Ident>, Vec<Type>, Vec<Option<String>>)> {
530 let mut has_receiver = false;
531 let mut names = Vec::new();
532 let mut types = Vec::new();
533 let mut qualifiers = Vec::new();
534 for input in &mut signature.inputs {
535 match input {
536 FnArg::Receiver(receiver) => {
537 if has_receiver || receiver.reference.is_none() || receiver.mutability.is_some() {
538 return Err(syn::Error::new_spanned(
539 receiver,
540 "a #[bean] method only supports an immutable &self receiver",
541 ));
542 }
543 has_receiver = true;
544 }
545 FnArg::Typed(argument) => {
546 let Pat::Ident(pattern) = argument.pat.as_ref() else {
547 return Err(syn::Error::new_spanned(
548 &argument.pat,
549 "use a simple parameter name for an injected dependency",
550 ));
551 };
552 names.push(pattern.ident.clone());
553 validate_dependency_type(argument.ty.as_ref())?;
554 types.push(argument.ty.as_ref().clone());
555 qualifiers.push(take_qualifier(&mut argument.attrs)?);
556 }
557 }
558 }
559 Ok((has_receiver, names, types, qualifiers))
560}
561
562fn take_qualifier(attributes: &mut Vec<syn::Attribute>) -> syn::Result<Option<String>> {
563 let mut value = None;
564 for attribute in attributes
565 .iter()
566 .filter(|attr| attr.path().is_ident("qualifier"))
567 {
568 if value.is_some() {
569 return Err(syn::Error::new_spanned(
570 attribute,
571 "only one qualifier is allowed",
572 ));
573 }
574 value = Some(attribute.parse_args::<LitStr>()?.value());
575 }
576 attributes.retain(|attr| !attr.path().is_ident("qualifier"));
577 Ok(value)
578}
579
580fn registration(
581 name: &syn::Ident,
582 output_type: &Type,
583 argument_names: &[syn::Ident],
584 dependency_types: &[Type],
585 qualifiers: &[Option<String>],
586 invocation: proc_macro2::TokenStream,
587) -> proc_macro2::TokenStream {
588 registration_with_options(
589 name,
590 output_type,
591 argument_names,
592 dependency_types,
593 qualifiers,
594 invocation,
595 &ProviderOptions::default(),
596 )
597}
598
599fn registration_with_options(
600 name: &syn::Ident,
601 output_type: &Type,
602 argument_names: &[syn::Ident],
603 dependency_types: &[Type],
604 qualifiers: &[Option<String>],
605 invocation: proc_macro2::TokenStream,
606 options: &ProviderOptions,
607) -> proc_macro2::TokenStream {
608 registration_with_prelude(
609 name,
610 output_type,
611 argument_names,
612 dependency_types,
613 qualifiers,
614 quote! {},
615 invocation,
616 options,
617 )
618}
619
620fn registration_with_prelude(
621 name: &syn::Ident,
622 output_type: &Type,
623 argument_names: &[syn::Ident],
624 dependency_types: &[Type],
625 qualifiers: &[Option<String>],
626 prelude: proc_macro2::TokenStream,
627 invocation: proc_macro2::TokenStream,
628 options: &ProviderOptions,
629) -> proc_macro2::TokenStream {
630 let factory_name = format_ident!("__di_factory_{name}");
631 let type_id_name = format_ident!("__di_type_id_{name}");
632 let type_name_name = format_ident!("__di_type_name_{name}");
633 let destroy_name = format_ident!("__di_destroy_{name}");
634 let option_tokens = |value: Option<&str>| match value {
635 Some(value) => quote!(Some(#value)),
636 None => quote!(None),
637 };
638 let bean_name = option_tokens(options.name.as_deref());
639 let primary = options.primary;
640 let eager = options.eager;
641 let profile = option_tokens(options.profile.as_deref());
642 let condition_key = option_tokens(options.condition_key.as_deref());
643 let condition_value = option_tokens(options.condition_value.as_deref());
644 let scope = match options.scope.as_deref().unwrap_or("singleton") {
645 "prototype" => quote!(::auto_di::Scope::Prototype),
646 "request" => quote!(::auto_di::Scope::Request),
647 _ => quote!(::auto_di::Scope::Singleton),
648 };
649 let post_construct = options.post_construct.as_ref().map(|method| {
650 let method = format_ident!("{method}");
651 quote! { value.#method().await; }
652 });
653 let (destroy_function, destroy_value) = if let Some(method) = &options.pre_destroy {
654 let method = format_ident!("{method}");
655 (
656 quote! {
657 #[doc(hidden)]
658 fn #destroy_name(value: ::auto_di::DynArc) -> ::auto_di::BoxFuture<'static, ::std::result::Result<(), ::auto_di::DiError>> {
659 ::std::boxed::Box::pin(async move {
660 let value = value.downcast::<#output_type>()
661 .map_err(|_| ::auto_di::DiError::TypeMismatch(::std::any::type_name::<#output_type>()))?;
662 value.#method().await;
663 Ok(())
664 })
665 }
666 },
667 quote!(Some(#destroy_name as _)),
668 )
669 } else {
670 (quote! {}, quote!(None))
671 };
672 let resolve_dependencies = argument_names
673 .iter()
674 .zip(dependency_types.iter())
675 .zip(qualifiers.iter())
676 .map(|((name, ty), qualifier)| dependency_resolution(name, ty, qualifier.as_deref()));
677
678 quote! {
679 #[doc(hidden)]
680 fn #type_id_name() -> ::std::any::TypeId {
681 ::std::any::TypeId::of::<#output_type>()
682 }
683
684 #[doc(hidden)]
685 fn #type_name_name() -> &'static str {
686 ::std::any::type_name::<#output_type>()
687 }
688
689 #[doc(hidden)]
690 fn #factory_name<'a>(
691 container: &'a ::auto_di::Container,
692 context: ::auto_di::ResolutionContext,
693 ) -> ::auto_di::BoxFuture<'a, ::std::result::Result<::auto_di::DynArc, ::auto_di::DiError>> {
694 ::std::boxed::Box::pin(async move {
695 #prelude
696 #(#resolve_dependencies)*
697 let value: #output_type = #invocation;
698 #post_construct
699 Ok(::std::sync::Arc::new(value) as ::auto_di::DynArc)
700 })
701 }
702
703 #destroy_function
704
705 ::auto_di::__private::inventory::submit! {
706 ::auto_di::ProviderDescriptor::configured(
707 #type_id_name,
708 #type_name_name,
709 #factory_name,
710 #bean_name,
711 #primary,
712 #scope,
713 #eager,
714 #profile,
715 #condition_key,
716 #condition_value,
717 #destroy_value,
718 )
719 }
720 }
721}
722
723fn singleton_type_ident(ty: &Type) -> syn::Result<&syn::Ident> {
724 let Type::Path(path) = ty else {
725 return Err(syn::Error::new_spanned(
726 ty,
727 "singleton impl type must be a named type",
728 ));
729 };
730 path.path
731 .segments
732 .last()
733 .map(|segment| &segment.ident)
734 .ok_or_else(|| syn::Error::new_spanned(ty, "singleton impl type must be a named type"))
735}
736
737fn validate_dependency_type(ty: &Type) -> syn::Result<()> {
738 if generic_inner(ty, "Arc").is_some()
739 || generic_inner(ty, "Provider").is_some()
740 || generic_inner(ty, "Lazy").is_some()
741 {
742 return Ok(());
743 }
744 if let Some(inner) = generic_inner(ty, "Option").or_else(|| generic_inner(ty, "Vec")) {
745 if generic_inner(inner, "Arc").is_some() {
746 return Ok(());
747 }
748 }
749 Err(syn::Error::new_spanned(
750 ty,
751 "dependency must be Arc<T>, Option<Arc<T>>, Vec<Arc<T>>, Provider<T>, or Lazy<T>",
752 ))
753}
754
755fn generic_inner<'a>(ty: &'a Type, expected: &str) -> Option<&'a Type> {
756 let Type::Path(path) = ty else { return None };
757 let segment = path.path.segments.last()?;
758 if segment.ident != expected {
759 return None;
760 }
761 let PathArguments::AngleBracketed(arguments) = &segment.arguments else {
762 return None;
763 };
764 match arguments.args.first() {
765 Some(GenericArgument::Type(inner)) if arguments.args.len() == 1 => Some(inner),
766 _ => None,
767 }
768}
769
770fn dependency_resolution(
771 name: &syn::Ident,
772 ty: &Type,
773 qualifier: Option<&str>,
774) -> proc_macro2::TokenStream {
775 if let Some(inner) = generic_inner(ty, "Arc") {
776 if matches!(inner, Type::TraitObject(_)) {
777 if let Some(qualifier) = qualifier {
778 return quote! {
779 let #name: #ty = (*container.resolve_named_dependency::<#ty>(#qualifier, &context).await?).clone();
780 };
781 }
782 return quote! {
783 let #name: #ty = (*container.resolve_dependency::<#ty>(&context).await?).clone();
784 };
785 }
786 if let Some(qualifier) = qualifier {
787 return quote! {
788 let #name: #ty = container.resolve_named_dependency::<#inner>(#qualifier, &context).await?;
789 };
790 }
791 return quote! {
792 let #name: #ty = container.resolve_dependency::<#inner>(&context).await?;
793 };
794 }
795 if let Some(wrapped) = generic_inner(ty, "Option") {
796 let inner = generic_inner(wrapped, "Arc").expect("validated Option<Arc<T>>");
797 return quote! {
798 let #name: #ty = container.resolve_optional_dependency::<#inner>(&context).await?;
799 };
800 }
801 if let Some(wrapped) = generic_inner(ty, "Vec") {
802 let inner = generic_inner(wrapped, "Arc").expect("validated Vec<Arc<T>>");
803 return quote! {
804 let #name: #ty = container.resolve_all_dependency::<#inner>(&context).await?;
805 };
806 }
807 quote! { let #name: #ty = ::std::default::Default::default(); }
808}