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