1pub use unsynn::Error as ParseError;
57pub use unsynn::ToTokens;
58
59use proc_macro2::TokenStream as TokenStream2;
60use unsynn::operator::names::{Assign, Colon, Comma, Gt, Lt, PathSep, Pound, RArrow, Semicolon};
61use unsynn::{
62 Any, BraceGroupContaining, BracketGroupContaining, CommaDelimitedVec, Cons, Either,
63 EndOfStream, Except, Ident, LiteralString, Many, Optional, ParenthesisGroupContaining, Parse,
64 ToTokenIter, TokenStream, keyword, operator, unsynn,
65};
66
67keyword! {
68 pub KAsync = "async";
69 pub KFn = "fn";
70 pub KTrait = "trait";
71 pub KSelfKw = "self";
72 pub KMut = "mut";
73 pub KDoc = "doc";
74 pub KPub = "pub";
75 pub KWhere = "where";
76}
77
78operator! {
79 pub Apostrophe = "'";
80}
81
82type VerbatimUntil<C> = Many<Cons<Except<C>, AngleTokenTree>>;
84
85unsynn! {
86 #[derive(Clone)]
88 pub struct AngleTokenTree(
89 pub Either<Cons<Lt, Vec<Cons<Except<Gt>, AngleTokenTree>>, Gt>, unsynn::TokenTree>,
90 );
91
92 pub struct RawAttribute {
93 pub _pound: Pound,
94 pub body: BracketGroupContaining<TokenStream>,
95 }
96
97 pub struct DocAttribute {
98 pub _doc: KDoc,
99 pub _assign: Assign,
100 pub value: LiteralString,
101 }
102
103 pub enum Visibility {
104 Pub(KPub),
105 PubRestricted(Cons<KPub, ParenthesisGroupContaining<TokenStream>>),
106 }
107
108 pub struct RefSelf {
109 pub _amp: unsynn::operator::names::And,
110 pub mutability: Option<KMut>,
111 pub name: KSelfKw,
112 }
113
114 pub struct MethodParam {
115 pub name: Ident,
116 pub _colon: Colon,
117 pub ty: Type,
118 }
119
120 pub struct GenericParams {
121 pub _lt: Lt,
122 pub params: VerbatimUntil<Gt>,
123 pub _gt: Gt,
124 }
125
126 #[derive(Clone)]
127 pub struct TypePath {
128 pub leading: Option<PathSep>,
129 pub first: Ident,
130 pub rest: Any<Cons<PathSep, Ident>>,
131 }
132
133 #[derive(Clone)]
134 pub enum Type {
135 Reference(TypeRef),
136 Tuple(TypeTuple),
137 PathWithGenerics(PathWithGenerics),
138 Path(TypePath),
139 }
140
141 #[derive(Clone)]
142 pub struct TypeRef {
143 pub _amp: unsynn::operator::names::And,
144 pub lifetime: Option<Cons<Apostrophe, Ident>>,
145 pub mutable: Option<KMut>,
146 pub inner: Box<Type>,
147 }
148
149 #[derive(Clone)]
150 pub struct TypeTuple(
151 pub ParenthesisGroupContaining<CommaDelimitedVec<Type>>,
152 );
153
154 #[derive(Clone)]
155 pub struct PathWithGenerics {
156 pub path: TypePath,
157 pub _lt: Lt,
158 pub args: CommaDelimitedVec<Type>,
159 pub _gt: Gt,
160 }
161
162 pub struct ReturnType {
163 pub _arrow: RArrow,
164 pub ty: Type,
165 }
166
167 pub struct WhereClause {
168 pub _where: KWhere,
169 pub bounds: VerbatimUntil<Semicolon>,
170 }
171
172 pub struct MethodParams {
173 pub receiver: RefSelf,
174 pub rest: Optional<Cons<Comma, CommaDelimitedVec<MethodParam>>>,
175 }
176
177 pub struct ServiceMethod {
178 pub attributes: Any<RawAttribute>,
179 pub _async: KAsync,
180 pub _fn: KFn,
181 pub name: Ident,
182 pub generics: Optional<GenericParams>,
183 pub params: ParenthesisGroupContaining<MethodParams>,
184 pub return_type: Optional<ReturnType>,
185 pub where_clause: Optional<WhereClause>,
186 pub _semi: Semicolon,
187 }
188
189 pub struct ServiceTrait {
190 pub attributes: Any<RawAttribute>,
191 pub vis: Optional<Visibility>,
192 pub _trait: KTrait,
193 pub name: Ident,
194 pub generics: Optional<GenericParams>,
195 pub body: BraceGroupContaining<Any<ServiceMethod>>,
196 pub _eos: EndOfStream,
197 }
198}
199
200impl Type {
205 pub fn as_result(&self) -> Option<(&Type, &Type)> {
207 match self {
208 Type::PathWithGenerics(PathWithGenerics { path, args, .. })
209 if path.last_segment().as_str() == "Result" && args.len() == 2 =>
210 {
211 let types = args.as_slice();
212 Some((&types[0].value, &types[1].value))
213 }
214 _ => None,
215 }
216 }
217
218 pub fn has_lifetime(&self) -> bool {
220 match self {
221 Type::Reference(TypeRef {
222 lifetime: Some(_), ..
223 }) => true,
224 Type::Reference(TypeRef { inner, .. }) => inner.has_lifetime(),
225 Type::PathWithGenerics(PathWithGenerics { args, .. }) => {
226 args.iter().any(|t| t.value.has_lifetime())
227 }
228 Type::Tuple(TypeTuple(group)) => group.content.iter().any(|t| t.value.has_lifetime()),
229 Type::Path(_) => false,
230 }
231 }
232
233 pub fn contains_channel(&self) -> bool {
238 match self {
239 Type::Reference(TypeRef { inner, .. }) => inner.contains_channel(),
240 Type::Tuple(TypeTuple(group)) => {
241 group.content.iter().any(|t| t.value.contains_channel())
242 }
243 Type::PathWithGenerics(PathWithGenerics { path, args, .. }) => {
244 let seg = path.last_segment();
245 if seg == "Tx" || seg == "Rx" {
246 return true;
247 }
248 args.iter().any(|t| t.value.contains_channel())
249 }
250 Type::Path(path) => {
251 let seg = path.last_segment();
252 seg == "Tx" || seg == "Rx"
253 }
254 }
255 }
256}
257
258impl TypePath {
263 pub fn last_segment(&self) -> String {
265 self.rest
266 .iter()
267 .last()
268 .map(|seg| seg.value.second.to_string())
269 .unwrap_or_else(|| self.first.to_string())
270 }
271}
272
273impl ServiceTrait {
278 pub fn name(&self) -> String {
280 self.name.to_string()
281 }
282
283 pub fn doc(&self) -> Option<String> {
285 collect_doc_string(&self.attributes)
286 }
287
288 pub fn methods(&self) -> impl Iterator<Item = &ServiceMethod> {
290 self.body.content.iter().map(|entry| &entry.value)
291 }
292}
293
294impl ServiceMethod {
299 pub fn name(&self) -> String {
301 self.name.to_string()
302 }
303
304 pub fn doc(&self) -> Option<String> {
306 collect_doc_string(&self.attributes)
307 }
308
309 pub fn args(&self) -> impl Iterator<Item = &MethodParam> {
311 self.params
312 .content
313 .rest
314 .iter()
315 .flat_map(|rest| rest.value.second.iter().map(|entry| &entry.value))
316 }
317
318 pub fn return_type(&self) -> Type {
320 self.return_type
321 .iter()
322 .next()
323 .map(|r| r.value.ty.clone())
324 .unwrap_or_else(unit_type)
325 }
326
327 pub fn is_mut_receiver(&self) -> bool {
329 self.params.content.receiver.mutability.is_some()
330 }
331
332 pub fn has_generics(&self) -> bool {
334 !self.generics.is_empty()
335 }
336}
337
338impl MethodParam {
343 pub fn name(&self) -> String {
345 self.name.to_string()
346 }
347}
348
349pub fn method_ok_and_err_types(return_ty: &Type) -> (&Type, Option<&Type>) {
356 if let Some((ok, err)) = return_ty.as_result() {
357 (ok, Some(err))
358 } else {
359 (return_ty, None)
360 }
361}
362
363fn unit_type() -> Type {
365 let mut iter = "()".to_token_iter();
366 Type::parse(&mut iter).expect("unit type should always parse")
367}
368
369fn collect_doc_string(attrs: &Any<RawAttribute>) -> Option<String> {
371 let mut docs = Vec::new();
372
373 for attr in attrs.iter() {
374 let mut body_iter = attr.value.body.content.clone().to_token_iter();
375 if let Ok(doc_attr) = DocAttribute::parse(&mut body_iter) {
376 let line = doc_attr.value.as_str().replace("\\\"", "\"");
377 docs.push(line);
378 }
379 }
380
381 if docs.is_empty() {
382 None
383 } else {
384 Some(docs.join("\n"))
385 }
386}
387
388#[allow(clippy::result_large_err)] pub fn parse_trait(tokens: &TokenStream2) -> Result<ServiceTrait, unsynn::Error> {
391 let mut iter = tokens.clone().to_token_iter();
392 ServiceTrait::parse(&mut iter)
393}