tabled_derive/
parse.rs
1use proc_macro2::{Ident, Span};
2use syn::{
3 parenthesized, parse::Parse, punctuated::Punctuated, token, Attribute, LitBool, LitInt, LitStr,
4 Token,
5};
6
7pub fn parse_attributes(
8 attributes: &[Attribute],
9) -> impl Iterator<Item = syn::Result<impl Iterator<Item = TabledAttr>>> + '_ {
10 attributes
11 .iter()
12 .filter(|attr| attr.path.is_ident("tabled"))
13 .map(|attr| attr.parse_args_with(Punctuated::<TabledAttr, Token![,]>::parse_terminated))
14 .map(|result| result.map(IntoIterator::into_iter))
15}
16
17pub struct TabledAttr {
18 pub ident: Ident,
19 pub kind: TabledAttrKind,
20}
21
22impl TabledAttr {
23 pub fn new(ident: Ident, kind: TabledAttrKind) -> Self {
24 Self { ident, kind }
25 }
26}
27
28#[derive(Clone)]
29pub enum TabledAttrKind {
30 Skip(LitBool),
31 Inline(LitBool, Option<LitStr>),
32 Rename(LitStr),
33 RenameAll(LitStr),
34 DisplayWith(LitStr, bool),
35 Order(LitInt),
36}
37
38impl Parse for TabledAttr {
39 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
40 use TabledAttrKind::*;
41
42 let name: Ident = input.parse()?;
43 let name_str = name.to_string();
44
45 if input.peek(Token![=]) {
46 let assign_token = input.parse::<Token![=]>()?;
47
48 if input.peek(LitStr) {
49 let lit = input.parse::<LitStr>()?;
50
51 match name_str.as_str() {
52 "rename" => return Ok(Self::new(name, Rename(lit))),
53 "rename_all" => return Ok(Self::new(name, RenameAll(lit))),
54 "display_with" => return Ok(Self::new(name, DisplayWith(lit, false))),
55 _ => {}
56 }
57 }
58
59 if input.peek(LitBool) {
60 let lit = input.parse::<LitBool>()?;
61
62 match name_str.as_str() {
63 "skip" => return Ok(Self::new(name, Skip(lit))),
64 "inline" => return Ok(Self::new(name, Inline(lit, None))),
65 _ => {}
66 }
67 }
68
69 if input.peek(LitInt) {
70 let lit = input.parse::<LitInt>()?;
71
72 if let "order" = name_str.as_str() {
73 return Ok(Self::new(name, Order(lit)));
74 }
75 }
76
77 return Err(syn::Error::new(
78 assign_token.span,
79 "expected `string literal` or `expression` after `=`",
80 ));
81 }
82
83 if input.peek(token::Paren) {
84 let nested;
85 let _paren = parenthesized!(nested in input);
86
87 if nested.peek(LitStr) {
88 let lit = nested.parse::<LitStr>()?;
89
90 match name_str.as_str() {
91 "display_with" => {
92 let use_self = if nested.peek(Token![,]) {
93 let _comma = nested.parse::<Token![,]>()?;
94 if nested.peek(syn::Ident) {
95 let ident = nested.parse::<syn::Ident>()?;
96 ident == "args"
97 } else {
98 false
99 }
100 } else {
101 false
102 };
103
104 return Ok(Self::new(name, DisplayWith(lit, use_self)));
105 }
106 "inline" => {
107 return Ok(Self::new(
108 name,
109 Inline(LitBool::new(true, Span::call_site()), Some(lit)),
110 ))
111 }
112 _ => {}
113 }
114 }
115
116 return Err(syn::Error::new(
117 _paren.span,
118 "expected a `string literal` in parenthesis",
119 ));
120 }
121
122 match name_str.as_str() {
123 "skip" => return Ok(Self::new(name, Skip(LitBool::new(true, Span::call_site())))),
124 "inline" => {
125 return Ok(Self::new(
126 name,
127 Inline(LitBool::new(true, Span::call_site()), None),
128 ))
129 }
130 _ => {}
131 }
132
133 Err(syn::Error::new(
134 name.span(),
135 format!("unexpected attribute: {}", name_str),
136 ))
137 }
138}