macro: support env vars in package attributes

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2021-10-13 11:43:49 +02:00
parent 9de065545b
commit 814014b874
3 changed files with 49 additions and 3 deletions

View File

@ -26,11 +26,11 @@ impl TryFrom<AttributeArgs> for ModuleAttrs {
.. ..
})) => { })) => {
if path.is_ident("name") { if path.is_ident("name") {
package_name = Some(litstr.value()); package_name = Some(expand_env_vars(&litstr)?);
} else if path.is_ident("file") { } else if path.is_ident("file") {
file_name = Some(litstr.value()); file_name = Some(expand_env_vars(&litstr)?);
} else if path.is_ident("lib") { } else if path.is_ident("lib") {
lib_name = Some(litstr.value()); lib_name = Some(expand_env_vars(&litstr)?);
} else { } else {
bail!(path => "unknown argument"); bail!(path => "unknown argument");
} }
@ -50,6 +50,39 @@ impl TryFrom<AttributeArgs> for ModuleAttrs {
} }
} }
fn expand_env_vars(lit_str: &syn::LitStr) -> Result<String, syn::Error> {
let input = lit_str.value();
let mut expanded = String::with_capacity(input.len());
let mut input = input.as_str();
loop {
let dollar = match input.find("${") {
Some(d) => d,
None => {
expanded.push_str(input);
break;
}
};
expanded.push_str(&input[..dollar]);
input = &input[(dollar + 2)..];
let end = input.find('}').ok_or_else(
|| format_err!(lit_str => "missing end of environment variable expansion"),
)?;
let var_name = &input[..end];
input = &input[(end + 1)..];
let var = std::env::var(var_name).map_err(|err| {
format_err!(lit_str => "failed to expand environment variable {:?}: {}", var_name, err)
})?;
expanded.push_str(&var);
}
Ok(expanded)
}
impl ModuleAttrs { impl ModuleAttrs {
pub fn mangle_package_name(&self) -> String { pub fn mangle_package_name(&self) -> String {
let mut out = String::with_capacity(self.package_name.len()); let mut out = String::with_capacity(self.package_name.len());

View File

@ -51,6 +51,8 @@ fn handle_error(mut item: TokenStream, data: Result<TokenStream, Error>) -> Toke
/// ``` /// ```
/// // 'lib' and 'file' are optional. We use 'file' here to prevent doc tests from writing out the /// // 'lib' and 'file' are optional. We use 'file' here to prevent doc tests from writing out the
/// // file. /// // file.
/// //
/// // 'name', 'lib' and 'file' expand environment variables such as `${CARGO_PKG_NAME}`
/// #[perlmod::package(name = "RSPM::Foo", lib = "perlmod_test", file = "/dev/null")] /// #[perlmod::package(name = "RSPM::Foo", lib = "perlmod_test", file = "/dev/null")]
/// mod an_unused_name { /// mod an_unused_name {
/// use anyhow::{bail, Error}; /// use anyhow::{bail, Error};

View File

@ -34,3 +34,14 @@ mod export {
Ok(value.0) Ok(value.0)
} }
} }
#[perlmod::package(name = "RSPM::EnvVarLibrary", lib = "x-${CARGO_PKG_NAME}-y")]
mod expanded_export {
use anyhow::Error;
#[export]
fn test_lib_env_vars(value: &str) -> Result<(), Error> {
println!("foo: {:?}", value);
Ok(())
}
}