]> git.proxmox.com Git - perlmod.git/blob - perlmod-macro/src/attribs.rs
add ability to set the errno value
[perlmod.git] / perlmod-macro / src / attribs.rs
1 use proc_macro2::{Ident, Span};
2
3 use syn::AttributeArgs;
4 use syn::Error;
5
6 pub struct ModuleAttrs {
7 pub package_name: String,
8 pub file_name: Option<String>,
9 pub lib_name: Option<String>,
10 pub write: Option<bool>,
11 }
12
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
24 impl TryFrom<AttributeArgs> for ModuleAttrs {
25 type Error = Error;
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;
31 let mut write = None;
32
33 for arg in args {
34 match arg {
35 syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
36 path,
37 lit: syn::Lit::Str(litstr),
38 ..
39 })) => {
40 if is_ident_check_dup(&path, &package_name, "name") {
41 package_name = Some(expand_env_vars(&litstr)?);
42 } else if is_ident_check_dup(&path, &file_name, "file") {
43 file_name = Some(expand_env_vars(&litstr)?);
44 } else if is_ident_check_dup(&path, &lib_name, "lib") {
45 lib_name = Some(expand_env_vars(&litstr)?);
46 } else {
47 error!(path => "unknown argument");
48 }
49 }
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 }
61 _ => error!(Span::call_site(), "unexpected attribute argument"),
62 }
63 }
64
65 let package_name = package_name
66 .ok_or_else(|| format_err!(Span::call_site(), "missing 'package' argument"))?;
67
68 Ok(Self {
69 package_name,
70 file_name,
71 lib_name,
72 write,
73 })
74 }
75 }
76
77 fn expand_env_vars(lit_str: &syn::LitStr) -> Result<String, Error> {
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
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
124 #[derive(Default)]
125 pub struct FunctionAttrs {
126 pub perl_name: Option<Ident>,
127 pub xs_name: Option<Ident>,
128 pub raw_return: bool,
129 pub cv_variable: Option<Ident>,
130 pub prototype: Option<String>,
131 pub serialize_error: bool,
132 pub errno: bool,
133 }
134
135 impl TryFrom<AttributeArgs> for FunctionAttrs {
136 type Error = Error;
137
138 fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> {
139 let mut attrs = FunctionAttrs::default();
140
141 for arg in args {
142 match arg {
143 syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
144 path,
145 lit: syn::Lit::Str(litstr),
146 ..
147 })) => {
148 if is_ident_check_dup(&path, &attrs.xs_name, "xs_name") {
149 attrs.xs_name = Some(Ident::new(&litstr.value(), litstr.span()));
150 } else if is_ident_check_dup(&path, &attrs.perl_name, "name") {
151 attrs.perl_name = Some(Ident::new(&litstr.value(), litstr.span()));
152 } else if is_ident_check_dup(&path, &attrs.prototype, "prototype") {
153 attrs.prototype = Some(litstr.value());
154 } else {
155 error!(path => "unknown argument");
156 continue;
157 }
158 }
159 syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
160 if path.is_ident("raw_return") {
161 attrs.raw_return = true;
162 } else if path.is_ident("serialize_error") {
163 attrs.serialize_error = true;
164 } else if path.is_ident("errno") {
165 attrs.errno = true;
166 } else {
167 error!(path => "unknown attribute");
168 }
169 }
170 _ => error!(Span::call_site(), "unexpected attribute argument"),
171 }
172 }
173
174 Ok(attrs)
175 }
176 }