1use proc_macro2::{Span, TokenStream, TokenTree};
2use quote::{ToTokens, TokenStreamExt};
3use syn::{
4 Attribute, Expr, Ident, Pat, Token,
5 parse::{Parse, ParseStream, Result},
6 punctuated::Punctuated,
7 token,
8};
9
10pub struct SpecArgs {
14 pub args: Punctuated<SpecArg, Token![,]>,
15}
16
17impl Parse for SpecArgs {
18 fn parse(input: ParseStream) -> Result<Self> {
19 Ok(Self {
20 args: Punctuated::<SpecArg, Token![,]>::parse_terminated(input)?,
21 })
22 }
23}
24
25pub struct SpecArg {
27 pub attrs: Vec<Attribute>,
28 pub keyword: Keyword,
29 pub keyword_span: Span,
30 pub colon: Token![:],
31 pub value: SpecArgValue,
32}
33
34impl Parse for SpecArg {
35 fn parse(input: ParseStream) -> Result<Self> {
36 let attrs = input.call(Attribute::parse_outer)?;
37 let (keyword, keyword_span) = Keyword::parse(input)?;
38 let colon = input.parse()?;
39 let value = match keyword {
40 Keyword::Binds => SpecArgValue::parse_pat_or_expr(input)?,
41 Keyword::Captures => SpecArgValue::Captures(input.parse()?),
42 _ => SpecArgValue::parse_expr_or_pat(input)?,
43 };
44
45 Ok(Self {
46 attrs,
47 keyword,
48 keyword_span,
49 colon,
50 value,
51 })
52 }
53}
54#[derive(Debug, Clone)]
61pub enum SpecArgValue {
62 Expr(Expr),
63 Pat(Pat),
64 Captures(Captures),
65}
66
67impl SpecArgValue {
68 pub fn try_into_expr(self) -> Result<Expr> {
70 if let Self::Expr(expr) = self {
71 return Ok(expr);
72 };
73 Err(syn::Error::new_spanned(self, "expected an expression"))
74 }
75
76 pub fn try_into_pat(self) -> Result<Pat> {
78 if let Self::Pat(pat) = self {
79 return Ok(pat);
80 };
81 Err(syn::Error::new_spanned(self, "expected a pattern"))
82 }
83
84 pub fn try_into_captures(self) -> Result<Captures> {
86 if let Self::Captures(captures) = self {
87 return Ok(captures);
88 };
89 Err(syn::Error::new_spanned(
90 self,
91 "expected captures: expression `as` pattern",
92 ))
93 }
94
95 fn parse_expr_or_pat(input: ParseStream) -> Result<Self> {
97 if let Ok(expr) = Self::parse_expr_or_nothing(input) {
98 Ok(Self::Expr(expr))
99 } else if let Ok(pat) = Self::parse_pat_or_nothing(input) {
100 Ok(Self::Pat(pat))
101 } else {
102 Err(input.error("expected an expression or a pattern"))
103 }
104 }
105
106 fn parse_pat_or_expr(input: ParseStream) -> Result<Self> {
108 if let Ok(pat) = Self::parse_pat_or_nothing(input) {
109 Ok(Self::Pat(pat))
110 } else if let Ok(expr) = Self::parse_expr_or_nothing(input) {
111 Ok(Self::Expr(expr))
112 } else {
113 Err(input.error("expected a pattern or an expression"))
114 }
115 }
116
117 fn parse_expr_or_nothing(input: ParseStream<'_>) -> Result<Expr> {
119 use syn::parse::discouraged::Speculative;
120 let fork = input.fork();
121 match Expr::parse(&fork) {
122 Ok(expr) => {
123 input.advance_to(&fork);
124 Ok(expr)
125 }
126 Err(err) => Err(err),
127 }
128 }
129
130 fn parse_pat_or_nothing(input: ParseStream<'_>) -> Result<Pat> {
132 use syn::parse::discouraged::Speculative;
133 let fork = input.fork();
134 match Pat::parse_single(&fork) {
135 Ok(pat) => {
136 input.advance_to(&fork);
137 Ok(pat)
138 }
139 Err(err) => Err(err),
140 }
141 }
142}
143
144impl ToTokens for SpecArgValue {
145 fn to_tokens(&self, tokens: &mut TokenStream) {
146 match self {
147 SpecArgValue::Expr(expr) => expr.to_tokens(tokens),
148 SpecArgValue::Pat(pat) => pat.to_tokens(tokens),
149 SpecArgValue::Captures(captures) => captures.to_tokens(tokens),
150 }
151 }
152}
153
154#[derive(Debug, Clone)]
157pub enum Captures {
158 One(Box<CaptureExpr>),
159 Many {
160 bracket: token::Bracket,
161 elems: Punctuated<CaptureExpr, Token![,]>,
162 },
163}
164
165impl Parse for Captures {
166 fn parse(input: ParseStream) -> Result<Self> {
167 if input.peek(token::Bracket) && !input.peek2(Token![as]) {
173 let content;
175 let bracket = syn::bracketed!(content in input);
176 let elems = Punctuated::parse_terminated(&content)?;
177 Ok(Captures::Many { bracket, elems })
178 } else {
179 Ok(Captures::One(input.parse()?))
181 }
182 }
183}
184
185impl ToTokens for Captures {
186 fn to_tokens(&self, tokens: &mut TokenStream) {
187 match self {
188 Self::One(capture_expr) => capture_expr.to_tokens(tokens),
189 Self::Many { bracket, elems } => bracket.surround(tokens, |tokens| {
190 elems.to_tokens(tokens);
191 }),
192 }
193 }
194}
195
196#[derive(Debug, Clone)]
198pub struct CaptureExpr {
199 pub expr: Option<Expr>,
200 pub as_: Option<Token![as]>,
201 pub pat: Option<Pat>,
202}
203
204impl ToTokens for CaptureExpr {
205 fn to_tokens(&self, tokens: &mut TokenStream) {
206 if let Some(expr) = &self.expr {
207 expr.to_tokens(tokens);
208 }
209 if let Some(as_) = &self.as_ {
210 as_.to_tokens(tokens);
211 }
212 if let Some(pat) = &self.pat {
213 pat.to_tokens(tokens);
214 }
215 }
216}
217
218impl Parse for CaptureExpr {
219 fn parse(input: ParseStream) -> Result<Self> {
220 let tokens_before_as = take_until_comma_or_last_as(input)?;
221 let expr = syn::parse::Parser::parse2(Expr::parse, tokens_before_as).ok();
222 let as_ = if input.peek(Token![as]) {
224 Some(input.parse()?)
225 } else {
226 None
227 };
228 let pat = SpecArgValue::parse_pat_or_nothing(input).ok();
229 Ok(Self { expr, as_, pat })
230 }
231}
232
233pub mod kw {
236 syn::custom_keyword!(requires);
237 syn::custom_keyword!(maintains);
238 syn::custom_keyword!(captures);
239 syn::custom_keyword!(binds);
240 syn::custom_keyword!(ensures);
241}
242
243#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
244pub enum Keyword {
245 Unknown(Ident),
246 Requires,
247 Maintains,
248 Captures,
249 Binds,
250 Ensures,
251}
252
253impl Keyword {
254 fn parse(input: ParseStream) -> Result<(Self, Span)> {
255 use Keyword::*;
256 Ok(if input.peek(kw::requires) {
257 let keyword: kw::requires = input.parse()?;
258 (Requires, keyword.span)
259 } else if input.peek(kw::maintains) {
260 let token: kw::maintains = input.parse()?;
261 (Maintains, token.span)
262 } else if input.peek(kw::captures) {
263 let token: kw::captures = input.parse()?;
264 (Captures, token.span)
265 } else if input.peek(kw::binds) {
266 let token: kw::binds = input.parse()?;
267 (Binds, token.span)
268 } else if input.peek(kw::ensures) {
269 let token: kw::ensures = input.parse()?;
270 (Ensures, token.span)
271 } else {
272 let ident: Ident = input.parse()?;
273 let span = ident.span();
274 (Unknown(ident), span)
275 })
276 }
277}
278
279impl std::fmt::Display for Keyword {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 match self {
282 Keyword::Requires => write!(f, "requires"),
283 Keyword::Maintains => write!(f, "maintains"),
284 Keyword::Captures => write!(f, "captures"),
285 Keyword::Binds => write!(f, "binds"),
286 Keyword::Ensures => write!(f, "ensures"),
287 Keyword::Unknown(ident) => write!(f, "{}", ident),
288 }
289 }
290}
291
292fn take_until_comma_or_last_as(input: ParseStream) -> Result<TokenStream> {
298 use syn::parse::discouraged::Speculative;
299 let fork = input.fork();
300 let mut peeked_tokens = TokenStream::new();
301 let mut consumed_tokens = TokenStream::new();
302 let mut has_seen_as = false;
303 while !fork.is_empty() && !fork.peek(Token![,]) {
304 if fork.peek(Token![as]) {
305 has_seen_as = true;
306 consumed_tokens.extend(peeked_tokens);
308 peeked_tokens = TokenStream::new();
309 input.advance_to(&fork);
310 }
311 let token: TokenTree = fork.parse()?;
312 peeked_tokens.append(token);
313 }
314 if has_seen_as {
315 Ok(consumed_tokens)
316 } else {
317 input.advance_to(&fork);
318 Ok(peeked_tokens)
319 }
320}