//! `status` command -- report WAL state, checkpoint, and directory layout. use serde::Serialize; use crate::{ CliError, EXIT_DEGRADED, commands::paths::DirsOutput, json::render_json, wal_state::{WalState, gather_wal_state}, }; /// `status` command output. /// /// `wal` carries the gathered [`WalState`] on success, or an `{"error": ...}` /// envelope when the WAL could not be inspected — modeled as an untagged enum /// so serde emits exactly one of the two shapes without a discriminant tag. #[derive(Serialize)] struct StatusOutput<'a> { version: &'a str, build_hash: &'a str, status: &'a str, wal: WalField, dirs: DirsOutput, } #[derive(Serialize)] #[serde(untagged)] enum WalField { State(WalState), Error { error: String }, } pub(crate) fn run(base: &std::path::Path, pretty: bool) -> Result<(String, i32), CliError> { let paths = tidaldb::Paths::new(base); let wal_dir = paths.wal_dir(); let version = env!("CARGO_PKG_VERSION"); let build_hash = tidaldb::BUILD_HASH; // Determine WAL state. let wal_result = gather_wal_state(&wal_dir); // An unreadable WAL (directory present but segments/checkpoint can't be // read) is degraded, not a clean empty store: exit code 2, mirroring // `diagnostics`. A script using `tidalctl status --path X && deploy` must // not treat a corrupt WAL as success. let (status, wal, exit_code) = match wal_result { Ok(wal) if wal.segments > 0 => ("ok", WalField::State(wal), 0), Ok(wal) => ("empty", WalField::State(wal), 0), Err(e) => ("error", WalField::Error { error: e }, EXIT_DEGRADED), }; let output = StatusOutput { version, build_hash, status, wal, dirs: DirsOutput::new(&paths), }; let rendered = render_json(&output, pretty)?; Ok((rendered, exit_code)) }