vite-static-derive 0.4.0

Derive macro support for `vite-static`
Documentation
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 })
}