1use crate::error::{spanned_compile_error, CompileError};
18use proc_macro2::{Span, TokenStream};
19use std::collections::HashMap;
20use syn::parse::Parser;
21#[allow(unused_imports)] use syn::spanned::Spanned;
23use syn::Meta;
24
25pub fn is_attribute(syn_attr: &syn::Attribute, attr: &str) -> bool {
26 get_attribute(syn_attr).eq(attr)
27}
28
29pub fn get_attribute(syn_attr: &syn::Attribute) -> String {
30 if syn_attr.meta.path().segments.len() != 1 {
31 "".to_owned()
32 } else {
33 syn_attr
34 .meta
35 .path()
36 .segments
37 .first()
38 .expect("missing segments")
39 .ident
40 .to_string()
41 }
42}
43
44pub fn has_attribute(attrs: &Vec<syn::Attribute>, attr: &str) -> bool {
45 attrs.iter().find(|a| is_attribute(a, attr)).is_some()
46}
47
48pub fn get_parenthesized_field_values(
49 meta: &Meta,
50) -> Result<HashMap<String, FieldValue>, TokenStream> {
51 match meta {
52 Meta::Path(_) => Ok(HashMap::new()),
53 Meta::List(list) => get_attribute_field_values(list.tokens.clone()),
54 Meta::NameValue(_) => {
55 panic!("list expected")
56 }
57 }
58}
59
60pub fn get_path(attr: &TokenStream) -> Result<syn::Path, TokenStream> {
61 if attr.is_empty() {
62 return spanned_compile_error(attr.span(), "path expected");
63 }
64 syn::parse2(attr.clone()).map_spanned_compile_error(attr.span(), "path expected")
65}
66
67#[derive(Debug, Clone)]
68#[allow(dead_code)]
69pub enum FieldValue {
70 StringLiteral(Span, String),
71 IntLiteral(Span, i64),
72 FloatLiteral(Span, f64),
73 BoolLiteral(Span, bool),
74 Path(Span, syn::Path),
75 Array(Span, Vec<FieldValue>),
76 FieldValues(Span, HashMap<String, FieldValue>),
77}
78
79impl FieldValue {
80 pub fn span(&self) -> Span {
81 match self {
82 FieldValue::StringLiteral(ref span, _) => span.clone(),
83 FieldValue::IntLiteral(ref span, _) => span.clone(),
84 FieldValue::FloatLiteral(ref span, _) => span.clone(),
85 FieldValue::BoolLiteral(ref span, _) => span.clone(),
86 FieldValue::Path(ref span, _) => span.clone(),
87 FieldValue::Array(ref span, _) => span.clone(),
88 FieldValue::FieldValues(ref span, _) => span.clone(),
89 }
90 }
91
92 pub fn get_paths(&self) -> Result<Vec<(syn::Path, Span)>, TokenStream> {
93 match self {
94 FieldValue::Path(ref span, ref path) => Ok(vec![(path.clone(), span.clone())]),
95 FieldValue::Array(_, ref array) => array
96 .iter()
97 .map(|f| {
98 if let FieldValue::Path(ref span, ref path) = f {
99 Ok((path.clone(), span.clone()))
100 } else {
101 spanned_compile_error(self.span(), "path expected")
102 }
103 })
104 .collect(),
105 _ => spanned_compile_error(self.span(), "path expected"),
106 }
107 }
108}
109
110pub fn get_attribute_field_values(
112 attr: TokenStream,
113) -> Result<HashMap<String, FieldValue>, TokenStream> {
114 let parser = syn::punctuated::Punctuated::<syn::FieldValue, syn::Token![,]>::parse_terminated;
115 if attr.is_empty() {
116 return Ok(HashMap::new());
117 }
118
119 let field_values = parser
120 .parse2(attr.clone())
121 .map_spanned_compile_error(attr.span(), "FieldValue (key: value, ...) expected")?;
122
123 parse_punctuated_field_values(&field_values)
124}
125
126fn parse_punctuated_field_values(
127 field_values: &syn::punctuated::Punctuated<syn::FieldValue, syn::Token![,]>,
128) -> Result<HashMap<String, FieldValue>, TokenStream> {
129 let mut result = HashMap::new();
130 for field in field_values.iter() {
131 if let syn::Member::Named(ref name) = field.member {
132 result.insert(
133 name.to_string(),
134 parse_field_value(&field.expr, field.span())?,
135 );
136 } else {
137 return spanned_compile_error(field.span(), "field should have named member");
138 }
139 }
140 Ok(result)
141}
142
143fn parse_field_value(expr: &syn::Expr, span: Span) -> Result<FieldValue, TokenStream> {
144 match expr {
145 syn::Expr::Lit(ref lit) => match lit.lit {
146 syn::Lit::Str(ref str_) => Ok(FieldValue::StringLiteral(str_.span(), str_.value())),
147 syn::Lit::Bool(ref bool_) => Ok(FieldValue::BoolLiteral(bool_.span(), bool_.value())),
148 syn::Lit::Int(ref int) => Ok(FieldValue::IntLiteral(
149 int.span(),
150 int.base10_parse::<i64>()
151 .map_spanned_compile_error(int.span(), "unable to parse integer to i64")?,
152 )),
153 syn::Lit::Float(ref float) => Ok(FieldValue::FloatLiteral(
154 float.span(),
155 float
156 .base10_parse::<f64>()
157 .map_spanned_compile_error(float.span(), "unable to parse integer to f64")?,
158 )),
159 _ => spanned_compile_error(span, &format!("unable to handle literal value {:?}", lit)),
160 },
161 syn::Expr::Path(ref path) => Ok(FieldValue::Path(span, path.path.clone())),
162 syn::Expr::Array(ref array) => {
163 let mut values: Vec<FieldValue> = Vec::new();
164 for expr in &array.elems {
165 values.push(parse_field_value(expr, expr.span())?);
166 }
167 Ok(FieldValue::Array(span, values))
168 }
169 syn::Expr::Struct(ref struct_) => Ok(FieldValue::FieldValues(
170 span,
171 parse_punctuated_field_values(&struct_.fields)?,
172 )),
173 _ => spanned_compile_error(span, &format!("invalid field value {:?}", expr)),
174 }
175}
176
177pub fn type_string(ty: &syn::Type) -> Result<String, TokenStream> {
178 if let syn::Type::Path(ref path) = ty {
179 let segments: Vec<String> = path
180 .path
181 .segments
182 .iter()
183 .map(|segment| segment.ident.to_string())
184 .collect();
185 Ok(segments.join("_"))
186 } else {
187 spanned_compile_error(ty.span(), &"path expected".to_string())
188 }
189}