1extern crate proc_macro;
2
3mod types;
4
5use proc_macro::TokenStream;
6use quote::{format_ident, quote};
7use types::InjectionType;
8
9struct 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![,]>()?; }
42 }
43 Ok(params)
44 }
45}
46
47#[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#[proc_macro_attribute]
66pub fn scope(_args: TokenStream, item: TokenStream) -> TokenStream {
67 item
68}
69
70#[proc_macro_attribute]
73pub fn interface(_args: TokenStream, item: TokenStream) -> TokenStream {
74 item
75}
76
77#[proc_macro_attribute]
80pub fn meta(_args: TokenStream, item: TokenStream) -> TokenStream {
81 item
82}
83
84fn 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
130fn 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 ¶ms.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#[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#[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 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
452fn implement_arg(
455 name: &syn::Ident,
456 typ: &syn::Type,
457 builder: &syn::Ident,
458 is_explicit: bool,
459) -> (
460 proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream, ) {
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 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 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 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 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 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 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
578fn 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
640fn 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
657fn 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
676fn 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
692fn 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
708fn 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
723fn 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
736fn 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}