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_client_crate() -> Result<proc_macro2::TokenStream, syn::Error> {
37 find_jsonrpsee_crate(&["jsonrpsee-http-client", "jsonrpsee-ws-client", "jsonrpsee-wasm-client"])
38}
39
40pub(crate) fn find_jsonrpsee_server_crate() -> Result<proc_macro2::TokenStream, syn::Error> {
42 find_jsonrpsee_crate(&["jsonrpsee-server"])
43}
44
45fn find_jsonrpsee_crate(crate_names: &[&str]) -> Result<proc_macro2::TokenStream, syn::Error> {
46 match crate_name("jsonrpsee") {
47 Ok(FoundCrate::Name(name)) => {
48 let ident = syn::Ident::new(&name, Span::call_site());
49 Ok(quote!(#ident))
50 }
51 Ok(FoundCrate::Itself) => panic!("Deriving RPC methods in any of the `jsonrpsee` crates is not supported"),
52 Err(_) => {
53 let mut err = None;
54 for name in crate_names {
55 match crate_name(name) {
56 Ok(FoundCrate::Name(name)) => {
57 let ident = syn::Ident::new(&name, Span::call_site());
58 return Ok(quote!(#ident));
59 }
60 Ok(FoundCrate::Itself) => {
61 err = Some(Err(syn::Error::new(
62 Span::call_site(),
63 "Deriving rpc methods in any of the `jsonrpsee` crates is not supported",
64 )));
65 }
66 Err(e) => {
67 err = Some(Err(syn::Error::new(Span::call_site(), &e)));
68 }
69 }
70 }
71
72 err.expect("Crate names must not be empty; this is a bug please open an issue")
73 }
74 }
75}
76
77pub(crate) fn generate_where_clause(
104 item_trait: &syn::ItemTrait,
105 sub_tys: &[syn::Type],
106 is_client: bool,
107 bounds: Option<&Punctuated<WherePredicate, Comma>>,
108) -> Vec<syn::WherePredicate> {
109 let visitor = visit_trait(item_trait, sub_tys);
110 let additional_where_clause = item_trait.generics.where_clause.clone();
111
112 if let Some(custom_bounds) = bounds {
113 let mut bounds: Vec<_> = additional_where_clause
114 .map(|where_clause| where_clause.predicates.into_iter().collect())
115 .unwrap_or_default();
116
117 bounds.extend(custom_bounds.iter().cloned());
118
119 return bounds;
120 }
121
122 item_trait
123 .generics
124 .type_params()
125 .map(|ty| {
126 let ty_path = syn::TypePath { qself: None, path: ty.ident.clone().into() };
127 let mut bounds: Punctuated<syn::TypeParamBound, Token![+]> = parse_quote!(Send + Sync + 'static);
128
129 if is_client {
130 if visitor.input_params.contains(&ty.ident) {
131 bounds.push(parse_quote!(jsonrpsee::core::Serialize))
132 }
133 if visitor.ret_params.contains(&ty.ident) || visitor.sub_params.contains(&ty.ident) {
134 bounds.push(parse_quote!(jsonrpsee::core::DeserializeOwned))
135 }
136 } else {
137 if visitor.input_params.contains(&ty.ident) {
138 bounds.push(parse_quote!(jsonrpsee::core::DeserializeOwned))
139 }
140 if visitor.ret_params.contains(&ty.ident) {
141 bounds.push(parse_quote!(std::clone::Clone))
142 }
143 if visitor.ret_params.contains(&ty.ident) || visitor.sub_params.contains(&ty.ident) {
144 bounds.push(parse_quote!(jsonrpsee::core::Serialize))
145 }
146 }
147
148 if let Some(where_clause) = &additional_where_clause {
150 for predicate in where_clause.predicates.iter() {
151 if let syn::WherePredicate::Type(where_ty) = predicate {
152 if let syn::Type::Path(ref predicate) = where_ty.bounded_ty {
153 if *predicate == ty_path {
154 bounds.extend(where_ty.bounds.clone().into_iter());
155 }
156 }
157 }
158 }
159 }
160
161 syn::WherePredicate::Type(syn::PredicateType {
162 lifetimes: None,
163 bounded_ty: syn::Type::Path(ty_path),
164 colon_token: <Token![:]>::default(),
165 bounds,
166 })
167 })
168 .collect()
169}
170
171fn visit_trait(item_trait: &syn::ItemTrait, sub_tys: &[syn::Type]) -> FindAllParams {
174 let type_params: HashSet<_> = item_trait.generics.type_params().map(|t| t.ident.clone()).collect();
175 let sub_tys = FindSubscriptionParams::new(type_params).visit(sub_tys);
176 let mut visitor = FindAllParams::new(sub_tys);
177 visitor.visit_item_trait(item_trait);
178 visitor
179}
180
181pub(crate) fn is_option(ty: &syn::Type) -> bool {
183 if let syn::Type::Path(path) = ty {
184 let mut it = path.path.segments.iter().peekable();
185 while let Some(seg) = it.next() {
186 if seg.ident == "Option" && it.peek().is_none() {
188 return true;
189 }
190 }
191 }
192
193 false
194}
195
196pub(crate) fn extract_doc_comments(attrs: &[syn::Attribute]) -> TokenStream2 {
201 let docs = attrs.iter().filter(|attr| match &attr.meta {
202 syn::Meta::NameValue(meta) => {
203 meta.path.is_ident("doc")
204 && matches!(meta.value, syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(_), .. }))
205 }
206 _ => false,
207 });
208 quote! ( #(#docs)* )
209}
210
211#[cfg(test)]
212mod tests {
213 use super::is_option;
214 use syn::parse_quote;
215
216 #[test]
217 fn is_option_works() {
218 assert!(is_option(&parse_quote!(Option<T>)));
219 assert!(is_option(&parse_quote!(Option)));
221 assert!(is_option(&parse_quote!(std::option::Option<R>)));
222 assert!(!is_option(&parse_quote!(foo::bar::Option::Booyah)));
223 }
224}