cgp_macro_lib/entrypoints/
cgp_getter.rs

1use std::collections::BTreeMap;
2use std::collections::btree_map::Entry;
3
4use proc_macro2::TokenStream;
5use quote::{ToTokens, quote};
6use syn::{Ident, ItemTrait, Type, parse_quote, parse2};
7
8use crate::derive_component::derive_component_with_ast;
9use crate::derive_getter::{
10    GetterField, derive_use_field_impl, derive_use_fields_impl, derive_with_provider_impl,
11    parse_getter_fields,
12};
13use crate::derive_provider::derive_is_provider_for;
14use crate::parse::{ComponentSpec, Entries};
15
16pub fn cgp_getter(attr: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
17    let mut entries = if let Ok(provider_ident) = parse2::<Ident>(attr.clone()) {
18        BTreeMap::from([("provider".to_owned(), provider_ident.to_token_stream())])
19    } else {
20        parse2::<Entries>(attr)?.entries
21    };
22
23    let consumer_trait: ItemTrait = syn::parse2(body)?;
24
25    let provider_entry = entries.entry("provider".to_owned());
26
27    if let Entry::Vacant(entry) = provider_entry {
28        let consumer_name = consumer_trait.ident.to_string();
29        if let Some(field_name) = consumer_name.strip_prefix("Has")
30            && !field_name.is_empty()
31        {
32            let provider_name =
33                Ident::new(&format!("{field_name}Getter"), consumer_trait.ident.span());
34            entry.insert(parse2(provider_name.to_token_stream())?);
35        }
36    }
37
38    let spec = ComponentSpec::from_entries(&entries)?;
39
40    let derived_component = derive_component_with_ast(&spec, consumer_trait.clone())?;
41
42    let fields = parse_getter_fields(&spec.context_type, &consumer_trait)?;
43
44    let use_fields_impl =
45        derive_use_fields_impl(&spec, &derived_component.provider_trait, &fields)?;
46
47    let component_name_type: Type = {
48        let component_name = &spec.component_name;
49        let component_params = &spec.component_params;
50        parse_quote!( #component_name < #component_params > )
51    };
52
53    let is_provider_use_fields_impl =
54        derive_is_provider_for(&component_name_type, &use_fields_impl)?;
55
56    let m_field: Option<[GetterField; 1]> = fields.try_into().ok();
57
58    let mut derived = quote! {
59        #derived_component
60
61        #use_fields_impl
62
63        #is_provider_use_fields_impl
64    };
65
66    if let Some([field]) = m_field {
67        let use_field_impl =
68            derive_use_field_impl(&spec, &derived_component.provider_trait, &field)?;
69        let is_provider_use_field_impl =
70            derive_is_provider_for(&component_name_type, &use_field_impl)?;
71
72        let use_provider_impl =
73            derive_with_provider_impl(&spec, &derived_component.provider_trait, &field)?;
74        let is_provider_use_provider_impl =
75            derive_is_provider_for(&component_name_type, &use_provider_impl)?;
76
77        derived.extend(quote! {
78            #use_field_impl
79            #is_provider_use_field_impl
80
81            #use_provider_impl
82            #is_provider_use_provider_impl
83        });
84    }
85
86    Ok(derived)
87}