Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ license-file = "LICENSE"
repository = "https://github.com/hyperpolymath/verisimiser"
keywords = ["verisim", "database", "augmentation", "drift-detection", "provenance"]
categories = ["command-line-utilities", "database"]
build = "build.rs"

[dependencies]
clap = { version = "4", features = ["derive"] }
Expand All @@ -20,5 +21,8 @@ thiserror = "2"
chrono = { version = "0.4", features = ["serde"] }
sha2 = "0.10"

[build-dependencies]
chrono = "0.4"

[dev-dependencies]
tempfile = "3"
39 changes: 39 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: PMPL-1.0-or-later
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
//
// Emit git-sha + build-date as compile-time env vars so the binary can show
// them in `verisimiser --version` and `verisimiser version --json`.
// Closes #56 (V-L3-J1). No build-dep — uses the `git` CLI and `chrono` is
// available at runtime via the main dependency tree.

use std::process::Command;

fn main() {
let sha = Command::new("git")
.args(["rev-parse", "--short=12", "HEAD"])
.output()
.ok()
.filter(|o| o.status.success())
.and_then(|o| String::from_utf8(o.stdout).ok())
.map(|s| s.trim().to_string())
.unwrap_or_else(|| "unknown".to_string());

let describe = Command::new("git")
.args(["describe", "--tags", "--always", "--dirty"])
.output()
.ok()
.filter(|o| o.status.success())
.and_then(|o| String::from_utf8(o.stdout).ok())
.map(|s| s.trim().to_string())
.unwrap_or_else(|| "unknown".to_string());

let build_date = chrono::Utc::now().format("%Y-%m-%d").to_string();

println!("cargo:rustc-env=VERISIMISER_GIT_SHA={}", sha);
println!("cargo:rustc-env=VERISIMISER_GIT_DESCRIBE={}", describe);
println!("cargo:rustc-env=VERISIMISER_BUILD_DATE={}", build_date);

// Re-run when HEAD moves or git ref changes.
println!("cargo:rerun-if-changed=.git/HEAD");
println!("cargo:rerun-if-changed=.git/refs");
}
45 changes: 42 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,19 @@ use anyhow::Result;
use clap::{Parser, Subcommand};
use verisimiser::{abi, codegen, manifest};

/// Long version string: `<crate-version> (<git-describe>, built <date>)`.
const LONG_VERSION: &str = concat!(
env!("CARGO_PKG_VERSION"),
" (",
env!("VERISIMISER_GIT_DESCRIBE"),
", built ",
env!("VERISIMISER_BUILD_DATE"),
")",
);

/// VeriSimiser — augment any database with VeriSimDB octad capabilities.
#[derive(Parser)]
#[command(name = "verisimiser", version, about, long_about = None)]
#[command(name = "verisimiser", version = LONG_VERSION, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
Expand Down Expand Up @@ -85,9 +95,18 @@ enum Commands {
Status {
#[arg(short, long, default_value = "verisimiser.toml")]
manifest: String,
/// Emit a structured JSON report instead of human-readable text.
#[arg(long)]
json: bool,
},
/// Show the octad modalities and which tiers they belong to.
Octad,
/// Print version, git-sha, and build-date.
Version {
/// Emit JSON instead of human-readable text.
#[arg(long)]
json: bool,
},
}

fn main() -> Result<()> {
Expand Down Expand Up @@ -189,16 +208,36 @@ fn main() -> Result<()> {
Ok(())
}

Commands::Status { manifest } => {
Commands::Status { manifest, json } => {
let m = manifest::load_manifest(&manifest)?;
manifest::print_status(&m);
if json {
let report = manifest::status_report(&m);
println!("{}", serde_json::to_string_pretty(&report)?);
} else {
manifest::print_status(&m);
}
Ok(())
}

Commands::Octad => {
print_octad();
Ok(())
}

Commands::Version { json } => {
if json {
let report = serde_json::json!({
"version": env!("CARGO_PKG_VERSION"),
"git_sha": env!("VERISIMISER_GIT_SHA"),
"git_describe": env!("VERISIMISER_GIT_DESCRIBE"),
"build_date": env!("VERISIMISER_BUILD_DATE"),
});
println!("{}", serde_json::to_string_pretty(&report)?);
} else {
println!("{}", LONG_VERSION);
}
Ok(())
}
}
}

Expand Down
66 changes: 66 additions & 0 deletions src/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,72 @@ mod init_template_tests {
}
}

/// Documented JSON schema returned by `verisimiser status --json`.
///
/// Field stability: `name`, `backend`, `sidecar_path`, `sidecar_storage`,
/// and `octad` are part of the public schema. New fields may be added
/// in minor versions; existing fields will not be removed without a
/// major version bump.
#[derive(Debug, Clone, Serialize)]
pub struct StatusReport {
/// Project name (`[project].name` or legacy `[verisimiser].name`).
pub name: String,
/// Effective database backend after legacy field resolution.
pub backend: String,
/// Path to the sidecar storage file.
pub sidecar_path: String,
/// Sidecar storage technology.
pub sidecar_storage: String,
/// Per-dimension enablement.
pub octad: OctadStatus,
}

/// Per-dimension boolean view used by `StatusReport`.
#[derive(Debug, Clone, Serialize)]
pub struct OctadStatus {
/// Number of enabled dimensions (always in `2..=8`).
pub enabled_count: usize,
/// Always `true`.
pub data: bool,
/// Always `true`.
pub metadata: bool,
pub provenance: bool,
pub lineage: bool,
pub constraints: bool,
pub access_control: bool,
pub temporal: bool,
pub simulation: bool,
}

/// Build a [`StatusReport`] from a loaded manifest.
///
/// Used by `verisimiser status --json`. The same content is rendered as
/// plain text by [`print_status`].
pub fn status_report(manifest: &Manifest) -> StatusReport {
let name = if !manifest.project.name.is_empty() {
manifest.project.name.clone()
} else {
manifest.verisimiser.name.clone()
};
StatusReport {
name,
backend: manifest.database.effective_backend().to_string(),
sidecar_path: manifest.sidecar.path.clone(),
sidecar_storage: manifest.sidecar.storage.clone(),
octad: OctadStatus {
enabled_count: manifest.octad.enabled_count(),
data: true,
metadata: true,
provenance: manifest.octad.enable_provenance,
lineage: manifest.octad.enable_lineage,
constraints: manifest.octad.enable_constraints,
access_control: manifest.octad.enable_access_control,
temporal: manifest.octad.enable_temporal,
simulation: manifest.octad.enable_simulation,
},
}
}

/// Print a human-readable status summary of a loaded manifest.
pub fn print_status(manifest: &Manifest) {
let name = if !manifest.project.name.is_empty() {
Expand Down
Loading