]>
Commit | Line | Data |
---|---|---|
abdf721d WB |
1 | use proc_macro2::{Ident, Span}; |
2 | ||
3 | use syn::AttributeArgs; | |
4b5b75f1 | 4 | use syn::Error; |
abdf721d WB |
5 | |
6 | pub struct ModuleAttrs { | |
7 | pub package_name: String, | |
06a18771 | 8 | pub file_name: Option<String>, |
abdf721d | 9 | pub lib_name: Option<String>, |
c15e1e14 | 10 | pub write: Option<bool>, |
abdf721d WB |
11 | } |
12 | ||
4b5b75f1 WB |
13 | fn is_ident_check_dup<T>(path: &syn::Path, var: &Option<T>, what: &'static str) -> bool { |
14 | if path.is_ident(what) { | |
15 | if var.is_some() { | |
16 | error!(path => "found multiple '{}' attributes", what); | |
17 | } | |
18 | true | |
19 | } else { | |
20 | false | |
21 | } | |
22 | } | |
23 | ||
abdf721d | 24 | impl TryFrom<AttributeArgs> for ModuleAttrs { |
f888c202 | 25 | type Error = Error; |
abdf721d WB |
26 | |
27 | fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> { | |
28 | let mut package_name = None; | |
29 | let mut file_name = None; | |
30 | let mut lib_name = None; | |
c15e1e14 | 31 | let mut write = None; |
abdf721d WB |
32 | |
33 | for arg in args { | |
34 | match arg { | |
35 | syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { | |
36 | path, | |
abdf721d | 37 | lit: syn::Lit::Str(litstr), |
f0475cd5 | 38 | .. |
abdf721d | 39 | })) => { |
4b5b75f1 | 40 | if is_ident_check_dup(&path, &package_name, "name") { |
814014b8 | 41 | package_name = Some(expand_env_vars(&litstr)?); |
4b5b75f1 | 42 | } else if is_ident_check_dup(&path, &file_name, "file") { |
814014b8 | 43 | file_name = Some(expand_env_vars(&litstr)?); |
4b5b75f1 | 44 | } else if is_ident_check_dup(&path, &lib_name, "lib") { |
814014b8 | 45 | lib_name = Some(expand_env_vars(&litstr)?); |
abdf721d | 46 | } else { |
449d7468 | 47 | error!(path => "unknown argument"); |
abdf721d WB |
48 | } |
49 | } | |
c15e1e14 WB |
50 | syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { |
51 | path, | |
52 | lit: syn::Lit::Bool(litbool), | |
53 | .. | |
54 | })) => { | |
55 | if is_ident_check_dup(&path, &write, "write") { | |
56 | write = Some(litbool.value()); | |
57 | } else { | |
58 | error!(path => "unknown argument"); | |
59 | } | |
60 | } | |
449d7468 | 61 | _ => error!(Span::call_site(), "unexpected attribute argument"), |
abdf721d WB |
62 | } |
63 | } | |
64 | ||
65 | let package_name = package_name | |
66 | .ok_or_else(|| format_err!(Span::call_site(), "missing 'package' argument"))?; | |
67 | ||
abdf721d WB |
68 | Ok(Self { |
69 | package_name, | |
70 | file_name, | |
71 | lib_name, | |
c15e1e14 | 72 | write, |
abdf721d WB |
73 | }) |
74 | } | |
75 | } | |
76 | ||
f888c202 | 77 | fn expand_env_vars(lit_str: &syn::LitStr) -> Result<String, Error> { |
814014b8 WB |
78 | let input = lit_str.value(); |
79 | let mut expanded = String::with_capacity(input.len()); | |
80 | ||
81 | let mut input = input.as_str(); | |
82 | loop { | |
83 | let dollar = match input.find("${") { | |
84 | Some(d) => d, | |
85 | None => { | |
86 | expanded.push_str(input); | |
87 | break; | |
88 | } | |
89 | }; | |
90 | ||
91 | expanded.push_str(&input[..dollar]); | |
92 | input = &input[(dollar + 2)..]; | |
93 | ||
94 | let end = input.find('}').ok_or_else( | |
95 | || format_err!(lit_str => "missing end of environment variable expansion"), | |
96 | )?; | |
97 | ||
98 | let var_name = &input[..end]; | |
99 | input = &input[(end + 1)..]; | |
100 | ||
101 | let var = std::env::var(var_name).map_err(|err| { | |
102 | format_err!(lit_str => "failed to expand environment variable {:?}: {}", var_name, err) | |
103 | })?; | |
104 | expanded.push_str(&var); | |
105 | } | |
106 | ||
107 | Ok(expanded) | |
108 | } | |
109 | ||
9525acd6 WB |
110 | impl ModuleAttrs { |
111 | pub fn mangle_package_name(&self) -> String { | |
112 | let mut out = String::with_capacity(self.package_name.len()); | |
113 | for ch in self.package_name.chars() { | |
114 | if ch.is_ascii_alphabetic() || ch.is_ascii_digit() { | |
115 | out.push(ch); | |
116 | } else { | |
117 | out.push('_'); | |
118 | } | |
119 | } | |
120 | out | |
121 | } | |
122 | } | |
123 | ||
2991a46a | 124 | #[derive(Default)] |
abdf721d | 125 | pub struct FunctionAttrs { |
2991a46a | 126 | pub perl_name: Option<Ident>, |
abdf721d | 127 | pub xs_name: Option<Ident>, |
29e61fa6 | 128 | pub raw_return: bool, |
77171723 | 129 | pub cv_variable: Option<Ident>, |
4b5b75f1 | 130 | pub prototype: Option<String>, |
2aa0c70d | 131 | pub serialize_error: bool, |
56bb74b1 | 132 | pub errno: bool, |
abdf721d WB |
133 | } |
134 | ||
135 | impl TryFrom<AttributeArgs> for FunctionAttrs { | |
f888c202 | 136 | type Error = Error; |
abdf721d WB |
137 | |
138 | fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> { | |
2991a46a | 139 | let mut attrs = FunctionAttrs::default(); |
abdf721d WB |
140 | |
141 | for arg in args { | |
142 | match arg { | |
143 | syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { | |
144 | path, | |
abdf721d | 145 | lit: syn::Lit::Str(litstr), |
f0475cd5 | 146 | .. |
abdf721d | 147 | })) => { |
4b5b75f1 | 148 | if is_ident_check_dup(&path, &attrs.xs_name, "xs_name") { |
2991a46a | 149 | attrs.xs_name = Some(Ident::new(&litstr.value(), litstr.span())); |
4b5b75f1 | 150 | } else if is_ident_check_dup(&path, &attrs.perl_name, "name") { |
2991a46a | 151 | attrs.perl_name = Some(Ident::new(&litstr.value(), litstr.span())); |
4b5b75f1 WB |
152 | } else if is_ident_check_dup(&path, &attrs.prototype, "prototype") { |
153 | attrs.prototype = Some(litstr.value()); | |
abdf721d | 154 | } else { |
449d7468 WB |
155 | error!(path => "unknown argument"); |
156 | continue; | |
abdf721d WB |
157 | } |
158 | } | |
29e61fa6 WB |
159 | syn::NestedMeta::Meta(syn::Meta::Path(path)) => { |
160 | if path.is_ident("raw_return") { | |
2991a46a | 161 | attrs.raw_return = true; |
2aa0c70d WB |
162 | } else if path.is_ident("serialize_error") { |
163 | attrs.serialize_error = true; | |
56bb74b1 WB |
164 | } else if path.is_ident("errno") { |
165 | attrs.errno = true; | |
29e61fa6 | 166 | } else { |
449d7468 | 167 | error!(path => "unknown attribute"); |
29e61fa6 WB |
168 | } |
169 | } | |
449d7468 | 170 | _ => error!(Span::call_site(), "unexpected attribute argument"), |
abdf721d WB |
171 | } |
172 | } | |
173 | ||
2991a46a | 174 | Ok(attrs) |
abdf721d WB |
175 | } |
176 | } |