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),
}
enum MacroOption {
ViteDist((String, Span)),
}
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> {
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:#}")))?;
Ok(MacroOption::ViteDist((value_str, value.span())))
} else {
Err(Error::new(
option.path.span(),
"expected Manifest macro option (available: vite_dist)",
))
}
}
_ => Err(Error::new(
meta.span(),
"expected name-value macro option (e.g. #[vite_dist = \"...\"])",
)),
}
}
pub fn parse_manifest_input(input: &DeriveInput) -> syn::Result<ManifestSettings> {
let mut vite_dist = ("./dist".to_string(), input.span());
for attr in &input.attrs {
let attr = parse_macro_option(&attr.meta)?;
match attr {
MacroOption::ViteDist(value) => vite_dist = value,
}
}
Ok(ManifestSettings { vite_dist })
}