sylvia_derive/types/
associated_types.rs1use 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#[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 pub fn as_names(&self) -> impl Iterator<Item = &Ident> {
29 self.0.iter().map(|associated| &associated.ident)
30 }
31
32 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 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 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 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 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
96pub 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
115pub 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}