liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
use std::error::Error;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Output};

#[cfg(not(test))]
fn main() {
    let metadata = match std::env::var("CARGO_MANIFEST_DIR") {
        Ok(directory) => build_metadata(Path::new(&directory)),
        Err(error) => BuildMetadata::unknown(&error),
    };
    if let Err(error) = write_metadata(&metadata, &mut io::stdout().lock()) {
        eprintln!("self-stream build metadata emission failed: {error}");
        std::process::exit(1);
    }
}

/// Emits the identity and the git references that invalidate this build.
///
/// # Errors
/// Returns an error if the build output cannot be written.
pub fn write_metadata(metadata: &BuildMetadata, output: &mut impl Write) -> io::Result<()> {
    writeln!(output, "cargo:rerun-if-changed=build.rs")?;
    for path in &metadata.watched {
        writeln!(output, "cargo:rerun-if-changed={}", path.display())?;
    }
    if let Some(warning) = &metadata.warning {
        writeln!(output, "cargo:warning=SELF_STREAM_BUILD_UNKNOWN: {warning}")?;
    }
    writeln!(
        output,
        "cargo:rustc-env=LIMINAL_BUILD_COMMIT={}",
        metadata.commit
    )
}

pub struct BuildMetadata {
    pub commit: String,
    pub watched: Vec<PathBuf>,
    pub warning: Option<String>,
}

impl BuildMetadata {
    fn unknown(error: &dyn std::fmt::Display) -> Self {
        Self {
            commit: "unknown".to_owned(),
            watched: Vec::new(),
            warning: Some(error.to_string().replace(['\n', '\r'], " ")),
        }
    }
}

/// Resolves identity within the workspace, retaining a named warning on failure.
#[must_use]
pub fn build_metadata(directory: &Path) -> BuildMetadata {
    match resolve_metadata(directory) {
        Ok(metadata) => metadata,
        Err(error) => BuildMetadata::unknown(&error),
    }
}

fn resolve_metadata(directory: &Path) -> Result<BuildMetadata, Box<dyn Error>> {
    let directory = std::fs::canonicalize(directory)?;
    let workspace = workspace_root(&directory)?;
    if !workspace.join(".git").try_exists()? {
        return Err("workspace has no git metadata".into());
    }
    let head = git_output(&workspace, &["rev-parse", "--verify", "HEAD"])?;
    let commit = std::str::from_utf8(&head)?.trim();
    if !matches!(commit.len(), 40 | 64) || !commit.bytes().all(|byte| byte.is_ascii_hexdigit()) {
        return Err("self-stream build metadata git HEAD is not a commit hash".into());
    }
    let mut watched = vec![git_path(&workspace, "HEAD")?];
    let symbolic = git_command(&workspace, &["symbolic-ref", "-q", "HEAD"])?;
    if symbolic.status.success() {
        let reference = std::str::from_utf8(&symbolic.stdout)?.trim();
        if !reference.starts_with("refs/") || reference.contains(['\n', '\r']) {
            return Err("self-stream build metadata HEAD reference is invalid".into());
        }
        watched.push(git_path(&workspace, reference)?);
    } else if symbolic.status.code() != Some(1) || !symbolic.stderr.is_empty() {
        return Err(git_failure(&symbolic).into());
    }
    Ok(BuildMetadata {
        commit: commit.to_owned(),
        watched,
        warning: None,
    })
}

fn workspace_root(directory: &Path) -> Result<PathBuf, Box<dyn Error>> {
    for ancestor in directory.ancestors() {
        let manifest = ancestor.join("Cargo.toml");
        if manifest.try_exists()? {
            let table: toml::Table = toml::from_str(&std::fs::read_to_string(manifest)?)?;
            if table.get("workspace").is_some_and(toml::Value::is_table) {
                return Ok(ancestor.to_path_buf());
            }
        }
    }
    Err("self-stream build metadata workspace root is absent".into())
}

fn git_path(workspace: &Path, name: &str) -> Result<PathBuf, Box<dyn Error>> {
    let output = git_output(workspace, &["rev-parse", "--git-path", name])?;
    let path = std::str::from_utf8(&output)?.trim();
    if path.is_empty() || path.contains(['\n', '\r']) {
        return Err("self-stream build metadata git watch path is invalid".into());
    }
    Ok(workspace.join(path))
}

fn git_command(directory: &Path, arguments: &[&str]) -> Result<Output, Box<dyn Error>> {
    Command::new("git")
        .current_dir(directory)
        .args(arguments)
        .output()
        .map_err(|error| {
            format!("self-stream build metadata git command could not start: {error}").into()
        })
}

fn git_failure(output: &Output) -> String {
    format!(
        "self-stream build metadata git command failed ({}): {}",
        output.status,
        String::from_utf8_lossy(&output.stderr).trim()
    )
}

fn git_output(directory: &Path, arguments: &[&str]) -> Result<Vec<u8>, Box<dyn Error>> {
    let output = git_command(directory, arguments)?;
    if !output.status.success() {
        return Err(git_failure(&output).into());
    }
    Ok(output.stdout)
}