use std::env;
use anyhow::Context;
use proc_macro2::Span;
use syn::{DeriveInput, Error, Expr, Lit, Meta, spanned::Spanned};
pub struct ManifestSettings {
pub vite_dist: (String, Span),
pub no_embedding: bool,
}
enum MacroOption {
ViteDist((String, Span)),
NoEmbedding,
}
fn resolve_env(value: String) -> anyhow::Result<String> {
if value.starts_with("env:") {
env::var(value.trim_start_matches("env:")).context("failed to get environment variable")
} else {
Ok(value)
}
}
fn parse_macro_option(meta: &Meta) -> syn::Result<MacroOption> {
#[allow(clippy::collapsible_match)] match meta {
Meta::NameValue(option) => {
if option.path.is_ident("vite_dist") {
let Expr::Lit(expr_lit) = &option.value else {
return Err(Error::new(option.value.span(), "expected string literal"));
};
let Lit::Str(value) = &expr_lit.lit else {
return Err(Error::new(option.value.span(), "expected string literal"));
};
let value_str = resolve_env(value.value())
.map_err(|err| Error::new(value.span(), format!("{err:#}")))?;
return Ok(MacroOption::ViteDist((value_str, value.span())));
}
}
Meta::Path(path) => {
if path.is_ident("no_embedding") {
return Ok(MacroOption::NoEmbedding);
}
}
_ => {}
}
Err(Error::new(
meta.path().span(),
"expected Manifest macro option (available: vite_dist = \"...\", no_embedding)",
))
}
pub fn parse_manifest_input(input: &DeriveInput) -> syn::Result<ManifestSettings> {
let mut vite_dist = ("./dist".to_string(), input.span());
let mut no_embedding = false;
for attr in &input.attrs {
let attr = parse_macro_option(&attr.meta)?;
match attr {
MacroOption::ViteDist(value) => vite_dist = value,
MacroOption::NoEmbedding => no_embedding = true,
}
}
Ok(ManifestSettings {
vite_dist,
no_embedding,
})
}