1use proc_macro2::{Group, Ident, Span};
2use std::convert::Into;
3use crate::parse_error;
5use proc_macro2::TokenStream;
6use quote::ToTokens;
7use std::collections::HashMap;
8use syn::{
9 parse::{Parse, ParseStream},
10 Expr, Token,
11};
12use syn::{Attribute, Error, Lit, LitBool};
13
14#[derive(Debug, Clone)]
15pub struct IdentWraper(proc_macro2::Ident);
16
17impl Parse for IdentWraper {
18 fn parse(input: ParseStream) -> syn::Result<Self> {
19 input.step(|cursor| {
20 if let Some((ident, rest)) = cursor.ident() {
21 let wraper = IdentWraper(proc_macro2::Ident::new(&ident.to_string(), ident.span()));
22 return Ok((wraper, rest));
23 }
24 Err(cursor.error("expected identifier"))
25 })
26 }
27}
28
29#[derive(Debug, Clone, parse_variants::Parse)]
30pub enum Item {
31 Identifier(syn::Ident),
32 IdentifierWraper(IdentWraper),
33 Literal(syn::LitInt),
34 String(syn::LitStr),
35}
36
37#[derive(Debug, Clone, parse_variants::Parse)]
38pub enum EvaluationValue {
39 Group(proc_macro2::Group),
40 Integer(syn::LitInt),
41 String(syn::LitStr),
42}
43
44#[derive(Debug, Clone, parse_variants::Parse)]
45pub enum AssignmentValue {
46 String(syn::LitStr),
47 Integer(syn::LitInt),
48 Boolean(syn::LitBool),
49 Group(proc_macro2::Group),
50}
51
52#[derive(Debug, Clone)]
53pub enum Value {
54 EvaluationValue(EvaluationValue),
55 AssignmentValue(AssignmentValue),
56}
57
58impl Value {
59 pub fn to_token_stream(&self) -> TokenStream {
60 match self {
61 Value::EvaluationValue(ev) => match ev {
62 EvaluationValue::Integer(lit_int) => lit_int.to_token_stream(),
63 EvaluationValue::String(lit_str) => lit_str.to_token_stream(),
64 EvaluationValue::Group(group) => group.stream(),
65 },
66 Value::AssignmentValue(av) => match av {
67 AssignmentValue::String(lit_str) => lit_str.to_token_stream(),
68 AssignmentValue::Integer(lit_int) => lit_int.to_token_stream(),
69 AssignmentValue::Boolean(lit_bool) => lit_bool.to_token_stream(),
70 AssignmentValue::Group(group) => group.stream(),
71 },
72 }
73 }
74}
75
76#[derive(Debug)]
77pub struct Args {
78 pub map: HashMap<Ident, Option<Value>>,
79}
80
81impl Default for Args {
82 fn default() -> Self {
83 Args::new()
84 }
85}
86
87impl Args {
88 pub fn new() -> Args {
89 Args {
90 map: HashMap::new(),
91 }
92 }
93
94 pub fn has(&self, ident: &str) -> bool {
95 let ident = Ident::new(ident, Span::call_site());
96 self.map.contains_key(&ident)
97 }
98
99 pub fn get(&self, ident: &str) -> Option<&Option<Value>> {
100 let ident = Ident::new(ident, Span::call_site());
101 self.map.get(&ident)
102 }
103
104 pub fn get_value_or<T: ToTokens>(
105 &self,
106 ident: &str,
107 field: T,
108 msg: &str,
109 ) -> Result<Option<Value>, TokenStream> {
110 let v = self.get(ident);
111 match v {
112 None => Ok(None),
113 Some(v) => match v {
114 Some(v) => Ok(Some(v.clone())),
115 None => Err(parse_error(field, msg)),
116 },
117 }
118 }
119
120 pub fn to_string_kv(&self) -> Vec<(String, String)> {
121 let mut list: Vec<(String, String)> = Vec::new();
122 for (k, v) in self.map.iter() {
123 let value = match v {
124 Some(value) => {
125 let v = value.to_token_stream();
126 let expr: Expr = syn::parse(v.into()).unwrap();
127 match &expr {
128 Expr::Lit(expr_lit) => match &expr_lit.lit {
129 Lit::Str(lit_str) => lit_str.value(),
130 _ => expr.to_token_stream().to_string(),
131 },
132 _ => expr.to_token_stream().to_string(),
133 }
134 }
135 None => "".to_string(),
136 };
137 list.push((k.to_string(), value));
138 }
139
140 list
141 }
142
143 pub fn allow(&self, list: &[&str]) -> syn::Result<()> {
144 for (ident, _) in self.map.iter() {
145 let name = ident.to_string();
146 if !list.contains(&name.as_str()) {
147 return Err(Error::new_spanned(
148 ident,
149 format!(
150 "unsupported attribute: {}, supported attributes are {}",
151 name,
152 list.join(", ")
153 ),
154 ));
155 }
156 }
157
158 Ok(())
159 }
160}
161
162fn advance_one_step(input: &ParseStream<'_>) {
163 let _ = input.step(|cursor| {
164 let rest = *cursor;
165 if let Some((_tt, next)) = rest.token_tree() {
166 Ok(((), next))
167 } else {
168 Ok(((), rest))
169 }
170 });
171}
172
173impl Parse for Args {
174 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
175 let mut map: HashMap<Ident, Option<Value>> = HashMap::new();
176 while !input.is_empty() {
177 let token_result = input.parse::<Item>();
178 if token_result.is_err() {
179 advance_one_step(&input);
180 if input.peek(Token![=]) {
181 let _: Token![=] = input.parse()?;
182 }
183 } else {
184 let token = token_result.ok().unwrap();
185 match token {
186 Item::Identifier(ident) | Item::IdentifierWraper(IdentWraper(ident)) => {
187 if input.peek(Token![,]) {
188 let _: Token![,] = input.parse()?;
189 map.insert(
190 ident,
191 Some(Value::AssignmentValue(AssignmentValue::Boolean(
192 LitBool::new(true, Span::call_site()),
193 ))),
194 );
195 } else if input.peek(Token![=]) {
196 let _: Token![=] = input.parse()?;
197 let rvalue: AssignmentValue = input.parse()?;
198 map.insert(ident, Some(Value::AssignmentValue(rvalue)));
199 } else {
200 let group: Group = input.parse()?;
201 map.insert(
202 ident,
203 Some(Value::EvaluationValue(EvaluationValue::Group(group))),
204 );
205 }
206
207 if input.peek(Token![,]) {
208 let _: Token![,] = input.parse()?;
209 }
210 }
211 Item::String(lit_str) => {
218 let default = Ident::new("default", Span::call_site());
219 map.entry(default).or_insert(Some(Value::EvaluationValue(
220 EvaluationValue::String(lit_str),
221 )));
222 }
223 _ => {
224 println!("invalid attributes");
225 let ident = Ident::new("", Span::call_site());
227 return Err(Error::new_spanned(ident, "invalid attributes".to_string()));
228 }
229 }
230 }
231 }
232
233 Ok(Self { map })
234 }
235}
236
237pub fn get_attributes(attr: &Attribute) -> Option<Args> {
238 let attributes: Option<Args> = attr.parse_args().ok();
239 attributes
240}