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