Skip to content

Commit 2f83303

Browse files
authored
Refactor and move check_components! implementation to cgp-macro-core (#239)
* Move check_components types * Draft implement new CheckEntry * Refactor to use new CheckEntry * Fix clippy * Fix span in delegate_and_check_components * Use TypeWithGenerics in EvaluatedCheckEntry * Migrate derive_check_components * Fully migrate derive check components
1 parent d4f3639 commit 2f83303

18 files changed

Lines changed: 512 additions & 403 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use syn::parse::{Parse, ParseStream};
2+
use syn::punctuated::Punctuated;
3+
use syn::token::Comma;
4+
5+
use crate::types::check_components::{CheckEntry, EvaluatedCheckEntry};
6+
7+
pub struct CheckEntries {
8+
pub entries: Punctuated<CheckEntry, Comma>,
9+
}
10+
11+
impl CheckEntries {
12+
pub fn eval(&self) -> Vec<EvaluatedCheckEntry> {
13+
let mut evaluated_entries = Vec::new();
14+
15+
for entry in &self.entries {
16+
evaluated_entries.extend(entry.eval());
17+
}
18+
19+
evaluated_entries
20+
}
21+
}
22+
23+
impl Parse for CheckEntries {
24+
fn parse(input: ParseStream) -> syn::Result<Self> {
25+
let entries: Punctuated<CheckEntry, Comma> = Punctuated::parse_terminated(input)?;
26+
27+
Ok(Self { entries })
28+
}
29+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use syn::parse::{Parse, ParseStream};
2+
use syn::spanned::Spanned;
3+
use syn::token::Colon;
4+
5+
use crate::types::check_components::{CheckKey, CheckValue, EvaluatedCheckEntry, TypeWithGenerics};
6+
7+
pub struct CheckEntry {
8+
pub key: CheckKey,
9+
pub value: Option<CheckValue>,
10+
}
11+
12+
impl CheckEntry {
13+
pub fn eval(&self) -> Vec<EvaluatedCheckEntry> {
14+
let mut entries = Vec::new();
15+
16+
let keys = self.key.to_keys();
17+
18+
let component_types_count = keys.len();
19+
20+
for component_type in keys.iter() {
21+
if let Some(value) = &self.value {
22+
let values = value.to_values();
23+
24+
if values.is_empty() {
25+
entries.push(EvaluatedCheckEntry {
26+
key: component_type.clone(),
27+
value: None,
28+
span: component_type.span(),
29+
})
30+
} else {
31+
let component_params_count = values.len();
32+
33+
for component_param in values.iter() {
34+
let component_param_type = &component_param.ty;
35+
let component_param_generics = &component_param.generics;
36+
37+
let span = if component_types_count >= component_params_count {
38+
component_type.span()
39+
} else {
40+
component_param_type.span()
41+
};
42+
43+
entries.push(EvaluatedCheckEntry {
44+
key: component_type.clone(),
45+
value: Some(TypeWithGenerics {
46+
ty: component_param_type.clone(),
47+
generics: component_param_generics.clone(),
48+
}),
49+
span,
50+
})
51+
}
52+
}
53+
} else {
54+
entries.push(EvaluatedCheckEntry {
55+
key: component_type.clone(),
56+
value: None,
57+
span: component_type.span(),
58+
})
59+
}
60+
}
61+
62+
entries
63+
}
64+
}
65+
66+
impl Parse for CheckEntry {
67+
fn parse(input: ParseStream) -> syn::Result<Self> {
68+
let key = input.parse()?;
69+
70+
if input.peek(Colon) {
71+
let _: Colon = input.parse()?;
72+
let value = input.parse()?;
73+
74+
Ok(Self {
75+
key,
76+
value: Some(value),
77+
})
78+
} else {
79+
Ok(Self { key, value: None })
80+
}
81+
}
82+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use proc_macro2::Span;
2+
use syn::Type;
3+
4+
use crate::types::check_components::TypeWithGenerics;
5+
6+
pub struct EvaluatedCheckEntry {
7+
pub key: Type,
8+
pub value: Option<TypeWithGenerics>,
9+
pub span: Span,
10+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use syn::parse::{Parse, ParseStream};
2+
use syn::punctuated::Punctuated;
3+
use syn::token::{Bracket, Comma};
4+
use syn::{Type, bracketed};
5+
6+
pub enum CheckKey {
7+
Single(Type),
8+
Multi(Punctuated<Type, Comma>),
9+
}
10+
11+
impl CheckKey {
12+
pub fn to_keys(&self) -> Vec<Type> {
13+
match self {
14+
Self::Single(key) => vec![key.clone()],
15+
Self::Multi(keys) => Vec::from_iter(keys.iter().cloned()),
16+
}
17+
}
18+
}
19+
20+
impl Parse for CheckKey {
21+
fn parse(input: ParseStream) -> syn::Result<Self> {
22+
if input.peek(Bracket) {
23+
let content;
24+
bracketed!(content in input);
25+
26+
let keys: Punctuated<Type, Comma> = Punctuated::parse_terminated(&content)?;
27+
Ok(Self::Multi(keys))
28+
} else {
29+
let key: Type = input.parse()?;
30+
Ok(Self::Single(key))
31+
}
32+
}
33+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
mod entries;
2+
mod entry;
3+
mod evaluated_check_entry;
4+
mod key;
5+
mod table;
6+
mod tables;
7+
mod type_with_generics;
8+
mod value;
9+
10+
pub use entries::*;
11+
pub use entry::*;
12+
pub use evaluated_check_entry::*;
13+
pub use key::*;
14+
pub use table::*;
15+
pub use tables::*;
16+
pub use type_with_generics::*;
17+
pub use value::*;
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
use proc_macro2::Span;
2+
use quote::ToTokens;
3+
use syn::parse::{Parse, ParseStream};
4+
use syn::punctuated::Punctuated;
5+
use syn::spanned::Spanned;
6+
use syn::token::{Comma, Lt, Pound, Where};
7+
use syn::{Attribute, Ident, Item, ItemImpl, ItemTrait, Type, WhereClause, braced, parse2};
8+
9+
use crate::functions::merge_generics;
10+
use crate::parse_internal;
11+
use crate::types::check_components::{CheckEntries, EvaluatedCheckEntry, TypeWithGenerics};
12+
use crate::types::generics::ImplGenerics;
13+
use crate::types::ident::IdentWithTypeArgs;
14+
15+
pub struct CheckComponentsTable {
16+
pub check_providers: Option<Punctuated<Type, Comma>>,
17+
pub impl_generics: ImplGenerics,
18+
pub trait_name: Ident,
19+
pub context_type: Type,
20+
pub where_clause: WhereClause,
21+
pub check_entries: CheckEntries,
22+
}
23+
24+
impl CheckComponentsTable {
25+
pub fn to_items(&self) -> syn::Result<Vec<Item>> {
26+
let (item_trait, item_impls) = self.eval()?;
27+
28+
let mut items = vec![item_trait.into()];
29+
items.extend(item_impls.into_iter().map(Into::into));
30+
31+
Ok(items)
32+
}
33+
34+
pub fn eval(&self) -> syn::Result<(ItemTrait, Vec<ItemImpl>)> {
35+
let mut item_impls = Vec::new();
36+
let unit: Type = parse_internal!(());
37+
38+
let context_type = &self.context_type;
39+
let trait_name = &self.trait_name;
40+
let impl_generics = &self.impl_generics;
41+
let where_clause = &self.where_clause;
42+
43+
let item_trait: ItemTrait = if self.check_providers.is_some() {
44+
parse_internal! {
45+
trait #trait_name <__Component__, __Params__: ?Sized>: IsProviderFor<__Component__, #context_type, __Params__> {}
46+
}
47+
} else {
48+
parse_internal! {
49+
trait #trait_name <__Component__, __Params__: ?Sized>: CanUseComponent<__Component__, __Params__> {}
50+
}
51+
};
52+
53+
let evaluated_entries = self.check_entries.eval();
54+
55+
for entry in evaluated_entries {
56+
let EvaluatedCheckEntry {
57+
key: component_type,
58+
value: component_params,
59+
span,
60+
} = entry;
61+
62+
let self_types = if let Some(check_providers) = &self.check_providers {
63+
Vec::from_iter(check_providers.iter().cloned())
64+
} else {
65+
// Override the span of the context type so that any unsatisfied constraint
66+
// error is highlighted on the component type instead
67+
let context_type = override_span(&span, context_type)?;
68+
vec![context_type]
69+
};
70+
71+
let TypeWithGenerics {
72+
ty: component_param,
73+
generics: check_generics,
74+
} = component_params.unwrap_or_else(|| unit.clone().into());
75+
76+
let generics = merge_generics(&check_generics.generics, &impl_generics.generics);
77+
78+
let impl_generics = generics.split_for_impl().0;
79+
80+
for self_type in self_types {
81+
let item_impl: ItemImpl = parse_internal! {
82+
impl #impl_generics
83+
#trait_name < #component_type, #component_param >
84+
for #self_type
85+
#where_clause
86+
{}
87+
};
88+
89+
item_impls.push(item_impl);
90+
}
91+
}
92+
93+
Ok((item_trait, item_impls))
94+
}
95+
}
96+
97+
impl Parse for CheckComponentsTable {
98+
fn parse(input: ParseStream) -> syn::Result<Self> {
99+
let mut check_providers: Option<Punctuated<Type, Comma>> = None;
100+
let mut m_check_trait_name: Option<Ident> = None;
101+
102+
if input.peek(Pound) {
103+
let attributes = input.call(Attribute::parse_outer)?;
104+
105+
for attribute in attributes {
106+
if attribute.path().is_ident("check_providers") {
107+
let provider_types: Punctuated<Type, Comma> =
108+
attribute.parse_args_with(Punctuated::parse_terminated)?;
109+
110+
check_providers
111+
.get_or_insert_default()
112+
.extend(provider_types);
113+
} else if attribute.path().is_ident("check_trait") {
114+
let check_trait_name: Ident = attribute.parse_args()?;
115+
116+
if m_check_trait_name.is_some() {
117+
return Err(syn::Error::new(
118+
attribute.span(),
119+
"Multiple `#[check_trait]` attributes found. Expected at most one.",
120+
));
121+
}
122+
123+
m_check_trait_name = Some(check_trait_name);
124+
} else {
125+
return Err(syn::Error::new(
126+
attribute.span(),
127+
format!("Invalid attribute {}", attribute.to_token_stream()),
128+
));
129+
}
130+
}
131+
};
132+
133+
let impl_generics = if input.peek(Lt) {
134+
input.parse()?
135+
} else {
136+
Default::default()
137+
};
138+
139+
let context_type: Type = input.parse()?;
140+
141+
let trait_name = if let Some(check_trait_name) = m_check_trait_name {
142+
check_trait_name
143+
} else {
144+
let context_type: IdentWithTypeArgs = parse2(context_type.to_token_stream())?;
145+
146+
Ident::new(
147+
&format!("__Check{}", context_type.ident),
148+
context_type.span(),
149+
)
150+
};
151+
152+
let where_clause = if input.peek(Where) {
153+
input.parse()?
154+
} else {
155+
WhereClause {
156+
where_token: Where(Span::call_site()),
157+
predicates: Punctuated::default(),
158+
}
159+
};
160+
161+
let content;
162+
braced!(content in input);
163+
164+
let entries: CheckEntries = content.parse()?;
165+
166+
Ok(Self {
167+
check_providers,
168+
impl_generics,
169+
trait_name,
170+
context_type,
171+
where_clause,
172+
check_entries: entries,
173+
})
174+
}
175+
}
176+
177+
fn override_span<T>(span: &Span, body: &T) -> syn::Result<T>
178+
where
179+
T: Parse + ToTokens,
180+
{
181+
parse2(
182+
body.to_token_stream()
183+
.into_iter()
184+
.map(|mut tree| {
185+
tree.set_span(*span);
186+
tree
187+
})
188+
.collect(),
189+
)
190+
}

0 commit comments

Comments
 (0)