git-slop 0.11.4

A deterministic repository token-defragmenter for humans and AI agents.
Documentation
use std::env;
use std::fs;
use std::path::Path;

use serde_json::Value;

fn valid_revision(revision: &str) -> bool {
    revision.len() == 40
        && revision
            .bytes()
            .all(|byte| byte.is_ascii_digit() || matches!(byte, b'a'..=b'f'))
}

fn explicit_identity() -> Result<Option<(String, bool)>, String> {
    let Ok(revision) = env::var("GIT_SLOP_SOURCE_REVISION") else {
        return Ok(None);
    };
    let revision = revision.trim();
    if revision.is_empty() {
        return Ok(None);
    }
    if !valid_revision(revision) {
        return Err("GIT_SLOP_SOURCE_REVISION must be a full lowercase commit id".into());
    }
    let dirty = env::var("GIT_SLOP_SOURCE_DIRTY")
        .map_err(|_| "GIT_SLOP_SOURCE_DIRTY is required with GIT_SLOP_SOURCE_REVISION")?
        .parse::<bool>()
        .map_err(|_| "GIT_SLOP_SOURCE_DIRTY must be true or false")?;
    Ok(Some((revision.to_owned(), dirty)))
}

fn packaged_identity() -> Result<Option<(String, bool)>, String> {
    if !Path::new(".cargo_vcs_info.json").is_file() {
        return Ok(None);
    }
    let bytes = fs::read(".cargo_vcs_info.json")
        .map_err(|error| format!("unable to read .cargo_vcs_info.json: {error}"))?;
    let payload: Value = serde_json::from_slice(&bytes)
        .map_err(|error| format!("unable to parse .cargo_vcs_info.json: {error}"))?;
    let git = payload
        .get("git")
        .and_then(Value::as_object)
        .ok_or_else(|| ".cargo_vcs_info.json must define git metadata".to_owned())?;
    let revision = git
        .get("sha1")
        .and_then(Value::as_str)
        .ok_or_else(|| ".cargo_vcs_info.json must define git.sha1".to_owned())?
        .trim();
    if !valid_revision(revision) {
        return Err(".cargo_vcs_info.json git.sha1 must be a full lowercase commit id".into());
    }
    let dirty = match git.get("dirty") {
        None => false,
        Some(value) => value.as_bool().ok_or_else(|| {
            ".cargo_vcs_info.json git.dirty must be boolean when present".to_owned()
        })?,
    };
    Ok(Some((revision.to_owned(), dirty)))
}

fn main() {
    println!("cargo::rerun-if-env-changed=GIT_SLOP_SOURCE_REVISION");
    println!("cargo::rerun-if-env-changed=GIT_SLOP_SOURCE_DIRTY");
    println!("cargo::rerun-if-changed=.cargo_vcs_info.json");

    let identity = explicit_identity()
        .and_then(|identity| match identity {
            Some(identity) => Ok(Some(identity)),
            None => packaged_identity(),
        })
        .unwrap_or_else(|error| panic!("invalid git-slop build provenance: {error}"));
    let (revision, dirty) = identity
        .map(|(revision, dirty)| (revision, Some(dirty)))
        .unwrap_or_default();
    println!("cargo::rustc-env=GIT_SLOP_SOURCE_REVISION={revision}");
    println!(
        "cargo::rustc-env=GIT_SLOP_SOURCE_DIRTY={}",
        dirty.map(|value| value.to_string()).unwrap_or_default()
    );
}