sylvia_derive/types/
associated_types.rs

1use proc_macro2::{Ident, TokenStream};
2use quote::quote;
3use syn::{parse_quote, ItemTrait, TraitItem, TraitItemType, Type, WhereClause, WherePredicate};
4
5pub const ERROR_TYPE: &str = "Error";
6pub const EXEC_TYPE: &str = "ExecC";
7pub const QUERY_TYPE: &str = "QueryC";
8
9/// Wrapper around associated types in parsed from a trait.
10#[derive(Default)]
11pub struct AssociatedTypes<'a>(Vec<&'a TraitItemType>);
12
13impl<'a> AssociatedTypes<'a> {
14    pub fn new(source: &'a ItemTrait) -> Self {
15        let associated_types: Vec<_> = source
16            .items
17            .iter()
18            .filter_map(|item| match item {
19                TraitItem::Type(ty) => Some(ty),
20                _ => None,
21            })
22            .collect();
23
24        Self(associated_types)
25    }
26
27    /// Returns [Iterator] over underlying [TraitItemType]s mapped to [Ident]s.
28    pub fn as_names(&self) -> impl Iterator<Item = &Ident> {
29        self.0.iter().map(|associated| &associated.ident)
30    }
31
32    /// Returns [Iterator] over underlying [TraitItemType]s without the `Error` type.
33    /// Used for generating generics for generated types.
34    pub fn without_error(&self) -> impl Iterator<Item = &TraitItemType> {
35        self.0
36            .iter()
37            .filter(|associated| associated.ident != ERROR_TYPE)
38            .cloned()
39    }
40
41    /// Returns [WherePredicate] from underlying [TraitItemType]s.
42    pub fn as_where_predicates(&self) -> Vec<WherePredicate> {
43        self.without_error()
44            .map(|associated| {
45                let name = &associated.ident;
46                let colon = &associated.colon_token;
47                let bound = &associated.bounds;
48                parse_quote! { #name #colon #bound }
49            })
50            .collect()
51    }
52
53    /// Returns [WhereClause] from underlying [TraitItemType]s.
54    pub fn as_where_clause(&self) -> Option<WhereClause> {
55        let predicates = self.as_where_predicates();
56        if !predicates.is_empty() {
57            parse_quote! { where #(#predicates),* }
58        } else {
59            None
60        }
61    }
62
63    /// Returns [WhereClause] from underlying [TraitItemType]s.
64    pub fn emit_contract_predicate(&self, trait_name: &Ident) -> TokenStream {
65        let predicate = quote! { ContractT: #trait_name };
66        if self.0.is_empty() {
67            return predicate;
68        }
69
70        let bounds = self.without_error().map(|associated| {
71            let name = &associated.ident;
72            quote! { #name = #name }
73        });
74
75        quote! {
76            #predicate < #(#bounds,)* >
77        }
78    }
79
80    /// Returns `Some(Type)` if `type_name` is present in the underlying associated types.
81    /// Returns `None` if it is not.
82    pub fn emit_contract_custom_type_accessor(
83        &self,
84        trait_name: &Ident,
85        type_name: &str,
86    ) -> Option<Type> {
87        self.as_names()
88            .find(|name| name.to_string().as_str() == type_name)
89            .map(|name| {
90                let type_name = Ident::new(type_name, name.span());
91                parse_quote! { <ContractT as #trait_name>:: #type_name}
92            })
93    }
94}
95
96/// Trait defining convertion to [Ident] and [WherePredicate].
97pub trait ItemType {
98    fn as_name(&self) -> &Ident;
99    fn as_where_predicate(&self) -> WherePredicate;
100}
101
102impl ItemType for TraitItemType {
103    fn as_name(&self) -> &Ident {
104        &self.ident
105    }
106
107    fn as_where_predicate(&self) -> WherePredicate {
108        let name = &self.ident;
109        let colon = &self.colon_token;
110        let bound = &self.bounds;
111        parse_quote! { #name #colon #bound }
112    }
113}
114
115/// Trait generating associated types.
116pub trait EmitAssociated {
117    fn emit_declaration(&self) -> Vec<TokenStream>;
118    fn emit_implementation(&self) -> Vec<TokenStream>;
119}
120
121impl EmitAssociated for WhereClause {
122    fn emit_declaration(&self) -> Vec<TokenStream> {
123        self.predicates
124            .iter()
125            .filter_map(|predicate| match predicate {
126                WherePredicate::Type(predicate) => {
127                    let bounded_ty = &predicate.bounded_ty;
128                    let bounds = &predicate.bounds;
129                    let lifetimes = &predicate.lifetimes.as_ref().map(|lf| {
130                        let lf = &lf.lifetimes;
131                        quote! { < #lf > }
132                    });
133                    Some(quote! { type #bounded_ty #lifetimes: #bounds; })
134                }
135                _ => None,
136            })
137            .collect()
138    }
139
140    fn emit_implementation(&self) -> Vec<TokenStream> {
141        self.predicates
142            .iter()
143            .filter_map(|predicate| match predicate {
144                WherePredicate::Type(predicate) => {
145                    let bounded_ty = &predicate.bounded_ty;
146                    let lifetimes = &predicate.lifetimes.as_ref().map(|lf| {
147                        let lf = &lf.lifetimes;
148                        quote! { < #lf > }
149                    });
150                    Some(quote! { type #bounded_ty #lifetimes = #bounded_ty; })
151                }
152                _ => None,
153            })
154            .collect()
155    }
156}