1use crate::extensions::*;
2use derive_more::From;
3use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
4use quote::{ToTokens, TokenStreamExt};
5use std::fmt::Display;
6use syn::{
7 ext::IdentExt,
8 parse::discouraged::Speculative,
9 token::{Brace, Bracket, Paren},
10 Expr, MacroDelimiter, MetaList, Path, Token,
11};
12
13#[derive(Debug, From)]
14pub enum Meta {
15 PathValue(PathValue),
17 PathList(PathList),
20 Expr(Expr),
22 Verbatim(TokenStream),
25}
26
27#[derive(Debug)]
28pub struct PathValue {
29 pub path: Path,
30 pub eq_token: Token![=],
31 pub value: Box<Meta>,
32}
33
34#[derive(Debug)]
35pub struct PathList {
36 pub path: Path,
37 pub eq_token: Option<Token![=]>,
38 pub delimiter: MacroDelimiter,
39 pub tokens: TokenStream,
40}
41
42impl Meta {
43 pub fn path(&self) -> syn::Result<&Path> {
44 match self {
45 Meta::PathList(pl) => Ok(&pl.path),
46 Meta::PathValue(pv) => Ok(&pv.path),
47 Meta::Expr(expr) => match expr {
48 Expr::Path(expr) => Ok(&expr.path),
49 _ => Err(expr.error("expected a path")),
50 },
51 Meta::Verbatim(tokens) => Err(tokens.error("expected a path")),
52 }
53 }
54
55 pub fn path_str(&self) -> String {
58 self.path().map_or("".into(), |path| path.to_string())
59 }
60
61 pub fn is_path_or_list(&self) -> bool {
62 matches!(self, Meta::PathList(_) | Meta::Expr(Expr::Path(_)))
63 }
64
65 pub fn is_path_or_empty_list(&self) -> bool {
66 match self {
67 Meta::PathList(pl) if pl.tokens.is_empty() => true,
68 Meta::Expr(Expr::Path(_)) => true,
69 _ => false,
70 }
71 }
72
73 pub fn as_path(&self) -> syn::Result<&Path> {
74 match self {
75 Meta::Expr(Expr::Path(expr)) => Ok(&expr.path),
76 Meta::PathList(pl) => Err(pl
77 .tokens_after_path()
78 .error("unexpected tokens, expected a single path")),
79 Meta::PathValue(pv) => Err(pv
80 .tokens_after_path()
81 .error("unexpected tokens, expected a single path")),
82 meta => Err(meta.error("expected a path")),
83 }
84 }
85
86 pub fn as_path_list(&self) -> syn::Result<&PathList> {
87 match self {
88 Meta::PathList(pl) => Ok(pl),
89 Meta::PathValue(pv) => Err(pv.value.error(format!(
90 "expected a list: `{} = (...)`",
91 pv.path.to_string()
92 ))),
93 Meta::Expr(Expr::Path(expr)) => Err(expr.error(format!(
94 "expected a list: `{0}(...)` or `{0} = (...)`",
95 expr.path.to_string()
96 ))),
97 meta => Err(meta
98 .error("expected a path followed by a list: `my_path(...)` or `my_path = (...)`")),
99 }
100 }
101
102 pub fn as_path_value(&self) -> syn::Result<&PathValue> {
103 match self {
104 Meta::PathValue(pv) => Ok(pv),
105 Meta::PathList(pl) => match pl.eq_token {
106 Some(_) => Err(pl.tokens.error("expected a single value, found a list")),
107 None => Err(pl.tokens.error(format!(
108 "expected `=` followed by a single value: `{} = ...`",
109 pl.path.to_string(),
110 ))),
111 },
112 Meta::Expr(Expr::Path(expr)) => Err(expr.error(format!(
113 "expected a value for that path: `{} = ...`",
114 expr.path.to_string()
115 ))),
116 meta => Err(meta.error("expected a path followed by a value: `my_path = ...`")),
117 }
118 }
119
120 pub fn as_value(&self) -> syn::Result<&Meta> {
121 self.as_path_value().map(|pv| pv.value.as_ref())
122 }
123
124 pub fn as_expr_or_value_expr(&self) -> syn::Result<&Expr> {
125 self.as_expr().or_else(|_| self.as_value()?.as_expr())
126 }
127
128 pub fn as_expr(&self) -> syn::Result<&Expr> {
129 match self {
130 Meta::Expr(expr) => Ok(expr),
131 meta => Err(meta.error("expected a valid expression")),
132 }
133 }
134
135 pub fn as_verbatim(&self, msg: impl Display) -> syn::Result<&TokenStream> {
136 match self {
137 Meta::Verbatim(tokens) => Ok(tokens),
138 meta => Err(meta.error(msg)),
139 }
140 }
141
142 pub fn assert_directive(&self, directive: &str) -> syn::Result<&Self> {
143 let path = self.path()?;
144 if !path.is_strict(directive) {
145 return Err(path.error(format!("expected #[codama({directive})] attribute")));
146 };
147 Ok(self)
148 }
149}
150
151impl PathValue {
152 pub fn tokens_after_path(&self) -> TokenStream {
154 let mut tokens = TokenStream::new();
155 self.eq_token.to_tokens(&mut tokens);
156 self.value.to_tokens(&mut tokens);
157 tokens
158 }
159}
160
161impl PathList {
162 pub fn tokens_after_path(&self) -> TokenStream {
164 let mut tokens = TokenStream::new();
165 self.eq_token.to_tokens(&mut tokens);
166 delimiters_to_tokens(&self.delimiter, &self.tokens, &mut tokens);
167 tokens
168 }
169
170 pub fn as_meta_list(&self) -> MetaList {
172 MetaList {
173 path: self.path.clone(),
174 delimiter: self.delimiter.clone(),
175 tokens: self.tokens.clone(),
176 }
177 }
178
179 pub fn each(&self, logic: impl FnMut(Meta) -> syn::Result<()>) -> syn::Result<()> {
181 self.as_meta_list().each(logic)
182 }
183
184 pub fn parse_metas(&self) -> syn::Result<Vec<Meta>> {
186 self.as_meta_list().parse_metas()
187 }
188
189 pub fn parse_comma_args<T: syn::parse::Parse>(&self) -> syn::Result<Vec<T>> {
191 self.as_meta_list().parse_comma_args()
192 }
193
194 pub fn as_expr_array(&self) -> syn::Result<syn::ExprArray> {
195 let syn::MacroDelimiter::Bracket(bracket_token) = self.delimiter else {
196 return Err(self.error("expected an array delimited with `[]`"));
197 };
198
199 Ok(syn::ExprArray {
200 attrs: vec![],
201 bracket_token,
202 elems: self.as_meta_list().parse_args_with(
203 syn::punctuated::Punctuated::<Expr, syn::Token![,]>::parse_terminated,
204 )?,
205 })
206 }
207}
208
209impl syn::parse::Parse for Meta {
210 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
211 let fork = input.fork();
212 match fork.call(parse_meta_path) {
213 Ok(path) => {
214 if fork.peek(Paren)
215 || fork.peek(Bracket)
216 || fork.peek(Brace)
217 || (fork.peek(Token![=])
218 && (fork.peek2(Paren) || fork.peek2(Bracket) || fork.peek2(Brace)))
219 {
220 Ok(Self::PathList(input.parse()?))
221 } else if fork.peek(Token![=]) {
222 Ok(Self::PathValue(input.parse()?))
223 } else {
224 input.advance_to(&fork);
225 Ok(Self::Expr(syn::Expr::Path(syn::ExprPath {
226 attrs: Vec::new(),
227 qself: None,
228 path,
229 })))
230 }
231 }
232 Err(_) => match fork.parse::<Expr>() {
233 Ok(expr) => {
234 input.advance_to(&fork);
235 Ok(Self::Expr(expr))
236 }
237 _ => Ok(Self::Verbatim(input.parse_arg()?)),
238 },
239 }
240 }
241}
242
243impl syn::parse::Parse for PathValue {
244 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
245 Ok(Self {
246 path: input.call(parse_meta_path)?,
247 eq_token: input.parse()?,
248 value: input.parse()?,
249 })
250 }
251}
252
253impl syn::parse::Parse for PathList {
254 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
255 let path = input.call(parse_meta_path)?;
256 let eq_token = input.parse()?;
257 let (delimiter, tokens) = input.call(parse_delimiters)?;
258 Ok(Self {
259 path,
260 eq_token,
261 delimiter,
262 tokens,
263 })
264 }
265}
266
267fn parse_meta_path(input: syn::parse::ParseStream) -> syn::Result<Path> {
270 let fork = input.fork();
271 let ident = syn::Ident::parse_any(&fork)?;
272 if ident == "true" || ident == "false" {
273 return Err(ident.error("unexpected reserved keyword"));
274 }
275 Ok(Path {
276 leading_colon: input.parse()?,
277 segments: {
278 let mut segments = syn::punctuated::Punctuated::new();
279 let ident = syn::Ident::parse_any(input)?;
280 segments.push_value(syn::PathSegment::from(ident));
281 while input.peek(Token![::]) {
282 let punct = input.parse()?;
283 segments.push_punct(punct);
284 let ident = syn::Ident::parse_any(input)?;
285 segments.push_value(syn::PathSegment::from(ident));
286 }
287 segments
288 },
289 })
290}
291
292fn parse_delimiters(input: syn::parse::ParseStream) -> syn::Result<(MacroDelimiter, TokenStream)> {
294 input.step(|cursor| match cursor.token_tree() {
295 Some((TokenTree::Group(g), rest)) => {
296 let span = g.delim_span();
297 let delimiter = match g.delimiter() {
298 Delimiter::Parenthesis => MacroDelimiter::Paren(Paren(span)),
299 Delimiter::Brace => MacroDelimiter::Brace(Brace(span)),
300 Delimiter::Bracket => MacroDelimiter::Bracket(Bracket(span)),
301 _ => return Err(cursor.error("expected delimiter")),
302 };
303 Ok(((delimiter, g.stream()), rest))
304 }
305 _ => Err(cursor.error("expected delimiter")),
306 })
307}
308
309fn delimiters_to_tokens(delimiter: &MacroDelimiter, inner: &TokenStream, tokens: &mut TokenStream) {
310 let (delim, span) = match delimiter {
311 MacroDelimiter::Paren(paren) => (Delimiter::Parenthesis, paren.span),
312 MacroDelimiter::Brace(brace) => (Delimiter::Brace, brace.span),
313 MacroDelimiter::Bracket(bracket) => (Delimiter::Bracket, bracket.span),
314 };
315 let mut group = Group::new(delim, inner.clone());
316 group.set_span(span.join());
317 tokens.append(group);
318}
319
320impl ToTokens for Meta {
321 fn to_tokens(&self, tokens: &mut TokenStream) {
322 match self {
323 Meta::PathList(m) => m.to_tokens(tokens),
324 Meta::PathValue(m) => m.to_tokens(tokens),
325 Meta::Expr(m) => m.to_tokens(tokens),
326 Meta::Verbatim(m) => m.to_tokens(tokens),
327 }
328 }
329}
330
331impl ToTokens for PathValue {
332 fn to_tokens(&self, tokens: &mut TokenStream) {
333 self.path.to_tokens(tokens);
334 self.eq_token.to_tokens(tokens);
335 self.value.to_tokens(tokens);
336 }
337}
338
339impl ToTokens for PathList {
340 fn to_tokens(&self, tokens: &mut TokenStream) {
341 self.path.to_tokens(tokens);
342 self.eq_token.to_tokens(tokens);
343 delimiters_to_tokens(&self.delimiter, &self.tokens, tokens);
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 macro_rules! meta {
352 ($($attr:tt)*) => {{
353 syn::parse_str::<Meta>(stringify!($($attr)*)).unwrap()
354 }};
355 }
356
357 #[test]
358 fn parse_path() {
359 let meta = meta! { foo };
360 let Meta::Expr(syn::Expr::Path(syn::ExprPath { path, .. })) = meta else {
361 panic!("expected Meta::Path");
362 };
363 assert!(path.is_strict("foo"));
364 }
365
366 #[test]
367 fn parse_path_list() {
368 let meta = meta! { foo(1, 2, 3) };
369 let Meta::PathList(meta) = meta else {
370 panic!("expected Meta::List");
371 };
372 assert!(meta.path.is_strict("foo"));
373 assert!(meta.eq_token.is_none());
374 assert_eq!(meta.tokens.to_string(), "1 , 2 , 3");
375 }
376
377 #[test]
378 fn parse_path_list_with_equal_sign() {
379 let meta = meta! { foo = [1, 2, 3] };
380 let Meta::PathList(meta) = meta else {
381 panic!("expected Meta::List");
382 };
383 assert!(meta.path.is_strict("foo"));
384 assert!(meta.eq_token.is_some());
385 assert_eq!(meta.tokens.to_string(), "1 , 2 , 3");
386 }
387
388 #[test]
389 fn parse_path_value_with_expression() {
390 let meta: Meta = meta! { foo = 42 };
391 let Meta::PathValue(meta) = meta else {
392 panic!("expected Meta::PathValue");
393 };
394 assert!(meta.path.is_strict("foo"));
395 let expr = meta.value.as_expr().unwrap();
396 assert_eq!(expr.as_unsigned_integer::<usize>().unwrap(), 42);
397 }
398
399 #[test]
400 fn parse_path_value_with_boolean() {
401 let meta: Meta = meta! { foo = true };
402 let Meta::PathValue(meta) = meta else {
403 panic!("expected Meta::PathValue");
404 };
405 assert!(meta.path.is_strict("foo"));
406 let expr = meta.value.as_expr().unwrap();
407 assert!(expr.as_bool().unwrap());
408 }
409
410 #[test]
411 fn parse_path_value_with_verbatim() {
412 let meta = meta! { foo = ?what? };
413 let Meta::PathValue(meta) = meta else {
414 panic!("expected Meta::PathValue");
415 };
416 assert!(meta.path.is_strict("foo"));
417 let value = meta.value.as_verbatim("expected verbatim").unwrap();
418 assert_eq!(value.to_string(), "? what ?");
419 }
420
421 #[test]
422 fn parse_path_value_with_list() {
423 let meta = meta! { foo = bar(1, 2, 3) };
424 let Meta::PathValue(meta) = meta else {
425 panic!("expected Meta::PathValue");
426 };
427 assert!(meta.path.is_strict("foo"));
428 let value = meta.value.as_path_list().unwrap();
429 assert!(value.path.is_strict("bar"));
430 assert_eq!(value.tokens.to_string(), "1 , 2 , 3");
431 }
432
433 #[test]
434 fn parse_expr() {
435 let meta = meta! { "hello" };
436 let Meta::Expr(expr) = meta else {
437 panic!("expected Meta::Expr");
438 };
439 assert_eq!(expr.to_token_stream().to_string(), "\"hello\"");
440 }
441
442 #[test]
443 fn parse_verbatim() {
444 let meta = meta! { [==> 42 <==] };
445 let Meta::Verbatim(verbatim) = meta else {
446 panic!("expected Meta::Verbatim");
447 };
448 assert_eq!(verbatim.to_string(), "[==> 42 <==]");
449 }
450
451 #[test]
452 fn parse_verbatim_list() -> syn::Result<()> {
453 let meta = meta! { foo([==> 1 <==], [==> 2 <==]) };
454 let list = meta.as_path_list()?;
455 assert_eq!(list.path.to_string(), "foo");
456 let metas = list.parse_metas()?;
457 assert_eq!(metas.len(), 2);
458 assert_eq!(
459 metas[0].as_verbatim("expected [==> n <==]")?.to_string(),
460 "[==> 1 <==]"
461 );
462 assert_eq!(
463 metas[1].as_verbatim("expected [==> n <==]")?.to_string(),
464 "[==> 2 <==]"
465 );
466 Ok(())
467 }
468
469 #[test]
470 fn path() -> syn::Result<()> {
471 assert_eq!(meta! { foo }.path()?.to_string(), "foo");
472 assert_eq!(meta! { foo(42) }.path()?.to_string(), "foo");
473 assert_eq!(meta! { foo = 42 }.path()?.to_string(), "foo");
474 assert_eq!(meta! { foo = bar(42) }.path()?.to_string(), "foo");
475 assert_eq!(
476 meta! { [verbatim] }.path().unwrap_err().to_string(),
477 "expected a path"
478 );
479 Ok(())
480 }
481
482 #[test]
483 fn is_path_or_empty_list() {
484 assert!(meta! { foo }.is_path_or_empty_list());
485 assert!(meta! { some_node }.is_path_or_empty_list());
486 assert!(meta! { foo() }.is_path_or_empty_list());
487 assert!(meta! { some_node() }.is_path_or_empty_list());
488 assert!(!meta! { foo = 42 }.is_path_or_empty_list());
489 assert!(!meta! { foo = bar(1, 2, 3) }.is_path_or_empty_list());
490 assert!(!meta! { foo(answer = 42) }.is_path_or_empty_list());
491 assert!(!meta! { some_node(hello) }.is_path_or_empty_list());
492 assert!(!meta! { 42 }.is_path_or_empty_list());
493 assert!(!meta! { [verbatim] }.is_path_or_empty_list());
494 }
495
496 #[test]
497 fn as_path() {
498 assert_eq!(meta! { foo }.as_path().unwrap().to_string(), "foo");
499
500 assert_eq!(
501 meta! { foo(42) }.as_path().unwrap_err().to_string(),
502 "unexpected tokens, expected a single path"
503 );
504 assert_eq!(
505 meta! { foo = 42 }.as_path().unwrap_err().to_string(),
506 "unexpected tokens, expected a single path"
507 );
508 assert_eq!(
509 meta! { foo = bar(42) }.as_path().unwrap_err().to_string(),
510 "unexpected tokens, expected a single path"
511 );
512 assert_eq!(
513 meta! { [verbatim] }.as_path().unwrap_err().to_string(),
514 "expected a path"
515 );
516 }
517
518 #[test]
519 fn as_path_list() {
520 let meta = meta! { foo(1, 2, 3) };
521 let list = meta.as_path_list().unwrap();
522 assert!(list.path.is_strict("foo"));
523 assert_eq!(list.tokens.to_string(), "1 , 2 , 3");
524
525 assert_eq!(
526 meta! { foo }.as_path_list().unwrap_err().to_string(),
527 "expected a list: `foo(...)` or `foo = (...)`"
528 );
529 assert_eq!(
530 meta! { foo = 42 }.as_path_list().unwrap_err().to_string(),
531 "expected a list: `foo = (...)`"
532 );
533 assert_eq!(
534 meta! { foo = bar(42) }
535 .as_path_list()
536 .unwrap_err()
537 .to_string(),
538 "expected a list: `foo = (...)`"
539 );
540 assert_eq!(
541 meta! { [verbatim] }.as_path_list().unwrap_err().to_string(),
542 "expected a path followed by a list: `my_path(...)` or `my_path = (...)`"
543 );
544 }
545}