cgp_component_macro_lib/derive_component/
component_spec.rs1use alloc::format;
2
3use proc_macro2::Span;
4use quote::ToTokens;
5use syn::parse::{Parse, ParseStream};
6use syn::punctuated::Punctuated;
7use syn::token::{Comma, Gt, Lt};
8use syn::{Error, Ident};
9
10use crate::derive_component::entry::Entries;
11
12pub struct ComponentSpec {
13 pub provider_name: Ident,
14 pub context_type: Ident,
15 pub component_name: Ident,
16 pub component_params: Punctuated<Ident, Comma>,
17}
18
19pub struct ComponentNameSpec {
20 pub component_name: Ident,
21 pub component_params: Punctuated<Ident, Comma>,
22}
23
24static VALID_KEYS: [&str; 3] = ["context", "provider", "name"];
25
26impl Parse for ComponentSpec {
27 fn parse(input: ParseStream) -> syn::Result<Self> {
28 let Entries { entries } = input.parse()?;
29
30 for key in entries.keys() {
31 if !VALID_KEYS.iter().any(|valid| valid == key) {
32 return Err(syn::Error::new(
33 Span::call_site(),
34 format!(
35 r#"invalid key in component spec: {key}. the following keys are valid: "context", "provider", "name"."#
36 ),
37 ));
38 }
39 }
40
41 let context_type: Ident = {
42 let raw_context_type = entries.get("context");
43
44 if let Some(context_type) = raw_context_type {
45 syn::parse2(context_type.to_token_stream())?
46 } else {
47 Ident::new("Context", Span::call_site())
48 }
49 };
50
51 let provider_name: Ident = {
52 let raw_provider_name = entries
53 .get("provider")
54 .ok_or_else(|| Error::new(input.span(), "expect provider name to be given"))?;
55
56 syn::parse2(raw_provider_name.to_token_stream())?
57 };
58
59 let (component_name, component_params) = {
60 let raw_component_name = entries.get("name");
61
62 if let Some(raw_component_name) = raw_component_name {
63 let ComponentNameSpec {
64 component_name,
65 component_params,
66 } = syn::parse2(raw_component_name.to_token_stream())?;
67 (component_name, component_params)
68 } else {
69 (
70 Ident::new(&format!("{}Component", provider_name), provider_name.span()),
71 Punctuated::default(),
72 )
73 }
74 };
75
76 Ok(ComponentSpec {
77 component_name,
78 provider_name,
79 context_type,
80 component_params,
81 })
82 }
83}
84
85impl Parse for ComponentNameSpec {
86 fn parse(input: ParseStream) -> syn::Result<Self> {
87 let component_name: Ident = input.parse()?;
88
89 let component_params = if input.peek(Lt) {
90 let _: Lt = input.parse()?;
91
92 let component_params: Punctuated<Ident, Comma> =
93 Punctuated::parse_separated_nonempty(input)?;
94
95 let _: Gt = input.parse()?;
96
97 component_params
98 } else {
99 Punctuated::default()
100 };
101
102 Ok(Self {
103 component_name,
104 component_params,
105 })
106 }
107}