]> git.proxmox.com Git - perlmod.git/blame - perlmod-macro/src/attribs.rs
add ability to set the errno value
[perlmod.git] / perlmod-macro / src / attribs.rs
CommitLineData
abdf721d
WB
1use proc_macro2::{Ident, Span};
2
3use syn::AttributeArgs;
4b5b75f1 4use syn::Error;
abdf721d
WB
5
6pub 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
13fn 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 24impl 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 77fn 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
110impl 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 125pub 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
135impl 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}