jsonrpsee_proc_macros/
helpers.rs1use std::collections::HashSet;
28
29use crate::visitor::{FindAllParams, FindSubscriptionParams};
30use proc_macro2::{Span, TokenStream as TokenStream2};
31use proc_macro_crate::{crate_name, FoundCrate};
32use quote::quote;
33use syn::{parse_quote, punctuated::Punctuated, token::Comma, visit::Visit, Token, WherePredicate};
34
35pub(crate) fn find_jsonrpsee_server_crate() -> Result<proc_macro2::TokenStream, syn::Error> {
37 find_jsonrpsee_crate(&["jsonrpsee-server"])
38}
39
40fn find_jsonrpsee_crate(crate_names: &[&str]) -> Result<proc_macro2::TokenStream, syn::Error> {
41 match crate_name("jsonrpsee") {
42 Ok(FoundCrate::Name(name)) => {
43 let ident = syn::Ident::new(&name, Span::call_site());
44 Ok(quote!(#ident))
45 }
46 Ok(FoundCrate::Itself) => {
47 panic!("Deriving RPC methods in any of the `jsonrpsee` crates is not supported")
48 }
49 Err(_) => {
50 let mut err = None;
51 for name in crate_names {
52 match crate_name(name) {
53 Ok(FoundCrate::Name(name)) => {
54 let ident = syn::Ident::new(&name, Span::call_site());
55 return Ok(quote!(#ident));
56 }
57 Ok(FoundCrate::Itself) => {
58 err = Some(Err(syn::Error::new(
59 Span::call_site(),
60 "Deriving rpc methods in any of the `jsonrpsee` crates is not supported",
61 )));
62 }
63 Err(e) => {
64 err = Some(Err(syn::Error::new(Span::call_site(), &e)));
65 }
66 }
67 }
68
69 err.expect("Crate names must not be empty; this is a bug please open an issue")
70 }
71 }
72}
73
74pub(crate) fn generate_where_clause(
103 item_trait: &syn::ItemTrait,
104 sub_tys: &[syn::Type],
105 bounds: Option<&Punctuated<WherePredicate, Comma>>,
106) -> Vec<syn::WherePredicate> {
107 let visitor = visit_trait(item_trait, sub_tys);
108 let additional_where_clause = item_trait.generics.where_clause.clone();
109
110 if let Some(custom_bounds) = bounds {
111 let mut bounds: Vec<_> = additional_where_clause
112 .map(|where_clause| where_clause.predicates.into_iter().collect())
113 .unwrap_or_default();
114
115 bounds.extend(custom_bounds.iter().cloned());
116
117 return bounds;
118 }
119
120 item_trait
121 .generics
122 .type_params()
123 .map(|ty| {
124 let ty_path = syn::TypePath { qself: None, path: ty.ident.clone().into() };
125 let mut bounds: Punctuated<syn::TypeParamBound, Token![+]> =
126 parse_quote!(Send + Sync + 'static);
127
128 if visitor.input_params.contains(&ty.ident) {
129 bounds.push(parse_quote!(jsonrpsee::core::DeserializeOwned))
130 }
131 if visitor.ret_params.contains(&ty.ident) {
132 bounds.push(parse_quote!(std::clone::Clone))
133 }
134 if visitor.ret_params.contains(&ty.ident) || visitor.sub_params.contains(&ty.ident) {
135 bounds.push(parse_quote!(jsonrpsee::core::Serialize))
136 }
137
138 if let Some(where_clause) = &additional_where_clause {
140 for predicate in where_clause.predicates.iter() {
141 if let syn::WherePredicate::Type(where_ty) = predicate &&
142 let syn::Type::Path(ref predicate) = where_ty.bounded_ty &&
143 *predicate == ty_path
144 {
145 bounds.extend(where_ty.bounds.clone());
146 }
147 }
148 }
149
150 syn::WherePredicate::Type(syn::PredicateType {
151 lifetimes: None,
152 bounded_ty: syn::Type::Path(ty_path),
153 colon_token: <Token![:]>::default(),
154 bounds,
155 })
156 })
157 .collect()
158}
159
160fn visit_trait(item_trait: &syn::ItemTrait, sub_tys: &[syn::Type]) -> FindAllParams {
163 let type_params: HashSet<_> =
164 item_trait.generics.type_params().map(|t| t.ident.clone()).collect();
165 let sub_tys = FindSubscriptionParams::new(type_params).visit(sub_tys);
166 let mut visitor = FindAllParams::new(sub_tys);
167 visitor.visit_item_trait(item_trait);
168 visitor
169}
170
171pub(crate) fn is_option(ty: &syn::Type) -> bool {
173 if let syn::Type::Path(path) = ty {
174 let mut it = path.path.segments.iter().peekable();
175 while let Some(seg) = it.next() {
176 if seg.ident == "Option" && it.peek().is_none() {
178 return true;
179 }
180 }
181 }
182
183 false
184}
185
186pub(crate) fn may_borrow(ty: &syn::Type) -> bool {
191 struct FindBorrow(bool);
192
193 impl<'ast> Visit<'ast> for FindBorrow {
194 fn visit_type_reference(&mut self, _: &'ast syn::TypeReference) {
195 self.0 = true;
196 }
197
198 fn visit_lifetime(&mut self, lifetime: &'ast syn::Lifetime) {
199 self.0 |= lifetime.ident != "static";
200 }
201 }
202
203 let mut visitor = FindBorrow(false);
204 visitor.visit_type(ty);
205 visitor.0
206}
207
208pub(crate) fn extract_doc_comments(attrs: &[syn::Attribute]) -> TokenStream2 {
213 let docs = attrs.iter().filter(|attr| match &attr.meta {
214 syn::Meta::NameValue(meta) => {
215 meta.path.is_ident("doc") &&
216 matches!(meta.value, syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(_), .. }))
217 }
218 _ => false,
219 });
220 quote! ( #(#docs)* )
221}
222
223#[cfg(test)]
224mod tests {
225 use super::{is_option, may_borrow};
226 use syn::parse_quote;
227
228 #[test]
229 fn is_option_works() {
230 assert!(is_option(&parse_quote!(Option<T>)));
231 assert!(is_option(&parse_quote!(Option)));
233 assert!(is_option(&parse_quote!(std::option::Option<R>)));
234 assert!(!is_option(&parse_quote!(foo::bar::Option::Booyah)));
235 }
236
237 #[test]
238 fn may_borrow_works() {
239 assert!(may_borrow(&parse_quote!(&str)));
240 assert!(may_borrow(&parse_quote!(Option<&'static RawValue>)));
241 assert!(may_borrow(&parse_quote!(Cow<'a, str>)));
242 assert!(!may_borrow(&parse_quote!(Cow<'static, str>)));
243 assert!(!may_borrow(&parse_quote!(Option<Vec<u64>>)));
244 }
245}