mnml-rs 0.2.13

A NvChad-style terminal IDE in Rust — vim or standard editing, LSP, git, and an embedded HTTP client.
//! Enumerate the `themes/*.toml` colour schemes and emit a
//! `THEME_SOURCES: &[(&str, &str)]` table (name → file contents, via
//! `include_str!`) for `src/ui/theme.rs` to parse at startup. Also embed
//! a build version (git short SHA + dirty suffix, with build epoch as a
//! fallback) into `MNML_GIT_SHA` so the statusline can show "you're running
//! commit X".

use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;

fn main() {
    emit_git_sha();
    // Expose `TARGET` (e.g. `aarch64-apple-darwin`) to the binary as
    // `MNML_TARGET` so the sibling-prebuilt installer can request the
    // matching artifact from each sibling repo's `latest-build`
    // release. Cargo sets `TARGET` in the build script env.
    println!(
        "cargo:rustc-env=MNML_TARGET={}",
        env::var("TARGET").unwrap_or_else(|_| "unknown".to_string())
    );
    let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("themes");
    println!("cargo:rerun-if-changed=themes");

    let mut entries: Vec<(String, String)> = Vec::new();
    if let Ok(rd) = fs::read_dir(&dir) {
        for e in rd.flatten() {
            let p = e.path();
            if p.extension().and_then(|x| x.to_str()) == Some("toml")
                && let Some(stem) = p.file_stem().and_then(|s| s.to_str())
            {
                entries.push((stem.to_string(), p.to_string_lossy().into_owned()));
            }
        }
    }
    entries.sort();

    let mut out = String::from(
        "// @generated by build.rs — the vendored base46 themes (name, file contents).\n\
         pub const THEME_SOURCES: &[(&str, &str)] = &[\n",
    );
    for (name, path) in &entries {
        out.push_str(&format!("    ({name:?}, include_str!({path:?})),\n"));
    }
    out.push_str("];\n");

    let dest = Path::new(&env::var("OUT_DIR").unwrap()).join("theme_sources.rs");
    fs::write(dest, out).expect("write theme_sources.rs");
}

/// `cargo:rustc-env=MNML_GIT_SHA=<sha>[-dirty]`. Re-run when HEAD moves or the
/// index changes so the SHA stays fresh. Falls back to the build-epoch seconds
/// if `git` is unavailable / the repo's missing.
fn emit_git_sha() {
    let git_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join(".git");
    // These cover branch HEAD moves + index/worktree mutations. (Cargo joins
    // them against CARGO_MANIFEST_DIR.)
    println!("cargo:rerun-if-changed=.git/HEAD");
    println!("cargo:rerun-if-changed=.git/index");
    if let Ok(head) = fs::read_to_string(git_dir.join("HEAD"))
        && let Some(rest) = head.strip_prefix("ref: ").map(str::trim)
    {
        println!("cargo:rerun-if-changed=.git/{rest}");
    }

    let sha = Command::new("git")
        .args(["rev-parse", "--short=9", "HEAD"])
        .current_dir(env!("CARGO_MANIFEST_DIR"))
        .output()
        .ok()
        .filter(|o| o.status.success())
        .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
        .filter(|s| !s.is_empty());
    let dirty = Command::new("git")
        .args(["status", "--porcelain"])
        .current_dir(env!("CARGO_MANIFEST_DIR"))
        .output()
        .ok()
        .filter(|o| o.status.success())
        .map(|o| !o.stdout.trim_ascii().is_empty())
        .unwrap_or(false);
    let version = match sha {
        Some(s) if dirty => format!("{s}-dirty"),
        Some(s) => s,
        None => {
            let secs = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .map(|d| d.as_secs())
                .unwrap_or(0);
            format!("build-{secs}")
        }
    };
    println!("cargo:rustc-env=MNML_GIT_SHA={version}");
}