lockjaw_processor/
parsing.rs

1/*
2Copyright 2020 Google LLC
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    https://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17use crate::error::{spanned_compile_error, CompileError};
18use proc_macro2::{Span, TokenStream};
19use std::collections::HashMap;
20use syn::parse::Parser;
21#[allow(unused_imports)] // somehow rust think this is unused.
22use 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
110/// Converts #[attr(key1 : "value1", key2 : value2)] to key-value map.
111pub 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}