enum_tools/parser/
params.rs

1use crate::parser::error::Error;
2use proc_macro2::{Ident, Span};
3use proc_macro_error::{abort, emit_error};
4use std::collections::HashMap;
5use syn::punctuated::Punctuated;
6use syn::spanned::Spanned;
7use syn::{Lit, Path, PathArguments, PathSegment, VisRestricted, Visibility};
8
9pub(crate) struct Params {
10    name: String,
11    span: Span,
12    params: HashMap<String, (Span, Option<Lit>)>,
13}
14
15impl Params {
16    pub(crate) fn new(path: Path) -> Params {
17        let (name, span) = Self::path_to_name_span(path);
18        Params {
19            name,
20            span,
21            params: HashMap::new(),
22        }
23    }
24
25    pub(crate) fn span(&self) -> Span {
26        self.span
27    }
28
29    pub(crate) fn name(&self) -> &str {
30        &self.name
31    }
32
33    pub(crate) fn insert(&mut self, path: Path, lit: Option<Lit>) -> bool {
34        let (name, span) = Self::path_to_name_span(path);
35        self.params.insert(name, (span, lit)).is_some()
36    }
37
38    fn path_to_name_span(path: Path) -> (String, Span) {
39        if path.leading_colon.is_none() && path.segments.len() == 1 {
40            let first = path.segments.first().unwrap();
41            if matches!(first.arguments, PathArguments::None) {
42                return (first.ident.to_string(), path.span());
43            }
44        }
45        abort!(path, Error::UnsupportedPath);
46    }
47
48    pub(crate) fn get_bool(&mut self, name: &str) -> bool {
49        if let Some((_, lit)) = self.params.remove(name) {
50            if let Some(lit) = lit {
51                emit_error!(lit.span(), Error::UnexpectedLiteral);
52                false
53            } else {
54                true
55            }
56        } else {
57            false
58        }
59    }
60
61    pub(crate) fn get_str_opt(&mut self, name: &str) -> Option<String> {
62        if let Some((path, lit)) = self.params.remove(name) {
63            if let Some(Lit::Str(l)) = lit {
64                Some(l.value())
65            } else {
66                emit_error!(
67                    lit.map_or_else(|| path.span(), |l| l.span()),
68                    Error::ExpectedLiteral("string")
69                );
70                None
71            }
72        } else {
73            None
74        }
75    }
76
77    pub(crate) fn get_vis_name(&mut self, default_name: &str) -> (Option<Visibility>, String) {
78        let vis = if let Some((path, lit)) = self.params.remove("vis") {
79            if let Some(Lit::Str(l)) = lit {
80                match l.value().as_str() {
81                    "" => Some(vis_inherited()),
82                    "pub(crate)" => Some(vis_pub_crate()),
83                    "pub" => Some(vis_pub()),
84                    _ => {
85                        emit_error!(l, Error::UnsupportedVisibility);
86                        None
87                    }
88                }
89            } else {
90                emit_error!(path, Error::ExpectedLiteral("string"));
91                None
92            }
93        } else {
94            None
95        };
96        let name = self
97            .get_str_opt("name")
98            .unwrap_or_else(|| default_name.to_string());
99        (vis, name)
100    }
101
102    pub(crate) fn finish<T>(self, value: T) -> T {
103        for (_, (param_path, _)) in self.params.into_iter() {
104            emit_error!(param_path, Error::UnknownParameter)
105        }
106        value
107    }
108}
109
110// helper to create Visibility
111
112const fn vis_inherited() -> Visibility {
113    Visibility::Inherited
114}
115
116fn vis_pub_crate() -> Visibility {
117    Visibility::Restricted(VisRestricted {
118        pub_token: Default::default(),
119        paren_token: Default::default(),
120        in_token: None,
121        path: Box::new(Path {
122            leading_colon: None,
123            segments: {
124                let mut path = Punctuated::new();
125                path.push_value(PathSegment {
126                    ident: Ident::new("crate", Span::call_site()),
127                    arguments: Default::default(),
128                });
129                path
130            },
131        }),
132    })
133}
134
135fn vis_pub() -> Visibility {
136    Visibility::Public(Default::default())
137}