arcature-cli 2026.2.0

Developer lifecycle CLI for Arcature applications.
Documentation
//! The Arcature framework version (AP2.1-10).
//!
//! Reads the framework version from the workspace `arcature` crate's
//! `Cargo.toml`. This is the honest source: the version the app was compiled
//! against is the workspace's `arcature` version. The CLI avoids a direct
//! runtime dependency on the `arcature` facade crate (it depends on templates
//! + db, not the facade), so it parses the workspace `Cargo.toml` instead.

use std::path::Path;

use super::error::PackageError;
use super::workspace::discover_workspace_root;

/// Read the Arcature framework version from the workspace `arcature`
/// crate's `Cargo.toml`. Walks up from the project root to find the
/// workspace root, then reads `crates/arcature/Cargo.toml`'s `version`.
pub(super) fn read_framework_version(app_root: &Path) -> Result<String, PackageError> {
    let workspace_root = discover_workspace_root(app_root);
    let arcature_cargo = workspace_root
        .join("crates")
        .join("arcature")
        .join("Cargo.toml");
    let text = std::fs::read_to_string(&arcature_cargo).map_err(|source| PackageError::Read {
        path: arcature_cargo.clone(),
        source,
    })?;
    let parsed: toml::Value = toml::from_str(&text).map_err(|source| PackageError::Toml {
        what: "arcature Cargo.toml",
        source,
    })?;
    let version = parsed
        .get("package")
        .and_then(|p| p.get("version"))
        .and_then(|v| v.as_str())
        .map(str::to_owned)
        .ok_or(PackageError::Missing {
            what: "arcature version",
            path: arcature_cargo,
        })?;
    Ok(version)
}