//! Operator-tool config + result types for the holger UI data layer (Tools#1–#5):
//! holger-ds delivery bench, migrate, airgap, and deploy (Skíðblaðnir). Each is a
//! plain serializable struct with a `state_json` so the robot asserts the pane as
//! DATA; the RUNNERS that populate them live on `UiData` in the parent. Split out of
//! `data`; `use super::*` inherits the parent's imports (Serialize / Value / …).
use super::*;
/// The result of one `holger-ds` artifact-delivery run, measured **over the
/// package protocol** (never in-process): file/crate counts plus the throughput
/// the gatling pool actually pulled off the wire. Surfaced in the Tools#1 pane and
/// `state_json` so the robot asserts a REAL benchmark ran (not a no-op).
#[derive(Debug, Clone, Serialize, Default, PartialEq)]
pub struct DsResult {
/// `true` once a run completed and populated the throughput figures.
pub ran: bool,
pub scale: String,
/// `"self-host"` or the external target base URL the run drove.
pub target: String,
pub crates: usize,
pub files: usize,
pub ops: u64,
pub bytes: u64,
pub mbs: f64,
pub ops_per_sec: f64,
/// One-line human status/result shown in the pane.
pub status: String,
pub error: Option<String>,
}
/// Tool #1 holger-ds configuration — the demo-plan scale slider + target. Drives
/// the artifact-delivery benchmark: `scale` (micro|small|large), and an optional
/// `target` base URL to measure an EXTERNAL holger/nexus/artifactory over the
/// real package protocol (empty ⇒ stand up a local holger gateway).
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct DsConfig {
/// `"micro"` / `"small"` / `"large"`.
pub scale: String,
/// External target base URL, or empty to self-host.
pub target: String,
/// One-line status shown in the pane (last run / running notice).
pub status: String,
/// The last completed run's result, if any.
pub result: Option<DsResult>,
}
impl Default for DsConfig {
fn default() -> Self {
Self {
scale: "micro".to_string(),
target: String::new(),
status: String::new(),
result: None,
}
}
}
impl DsConfig {
pub fn state_json(&self) -> Value {
serde_json::to_value(self).unwrap_or(Value::Null)
}
/// The equivalent `holger-server holger-ds` CLI invocation — the plan the
/// Tools#1 pane shows (and a robot asserts).
pub fn cli_command(&self) -> String {
let mut s = format!("holger-server holger-ds --preset {}", self.scale);
if !self.target.is_empty() {
s.push_str(&format!(" --target {}", self.target));
}
s
}
}
/// The result of one Migrate ("suck") run via the holger agent connector:
/// how many upstream repositories were dumped into the znippy archive (or a clear
/// error). Surfaced in the Tools#2 pane + `state_json` so the robot asserts the
/// connector was actually invoked (not a label-only stub).
#[derive(Debug, Clone, Serialize, Default, PartialEq, Eq)]
pub struct MigrateRun {
/// `true` once the connector ran and produced an archive.
pub ran: bool,
pub source: String,
pub url: String,
pub output: String,
/// Number of upstream repositories sucked into the archive.
pub repositories: usize,
pub status: String,
pub error: Option<String>,
}
/// Tool #2 Migrate ("suck") configuration — pull a Nexus / Artifactory registry
/// into one or many znippy archives via the holger agent (the single home of the
/// sucking). `source` is `"nexus"` or `"artifactory"`; `url` the registry base;
/// `output` the `.znippy` path; `repository` an optional single-repo filter.
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct MigrateConfig {
/// `"nexus"` or `"artifactory"`.
pub source: String,
pub url: String,
pub output: String,
pub repository: String,
/// One-line status shown in the pane (last run / running notice).
pub status: String,
/// The last completed run's result, if any.
pub result: Option<MigrateRun>,
}
impl Default for MigrateConfig {
fn default() -> Self {
Self {
source: "nexus".to_string(),
url: String::new(),
output: "migrated.znippy".to_string(),
repository: String::new(),
status: String::new(),
result: None,
}
}
}
impl MigrateConfig {
pub fn state_json(&self) -> Value {
serde_json::to_value(self).unwrap_or(Value::Null)
}
/// The equivalent `holger-agent` invocation — the migrate plan the Tools#2
/// pane shows (and a robot asserts). Uses `--<source>-url` so it stays in
/// lockstep with the agent CLI flags.
pub fn cli_command(&self) -> String {
let url_flag = if self.source == "artifactory" { "--artifactory-url" } else { "--nexus-url" };
let url = if self.url.is_empty() { "<url>" } else { &self.url };
let mut s = format!(
"holger-agent --from {} {url_flag} {url} --to znippy --output {}",
self.source, self.output
);
if !self.repository.is_empty() {
s.push_str(&format!(" --repository {}", self.repository));
}
s
}
}
/// The result of one Tool #5 Airgap populate run. A `--dry-run` produces the plan
/// only (no shell); a real run delegates to the `distribution/airgap/populate`
/// driver and captures its log + exit status. Surfaced in the Tools#5 pane +
/// `state_json` so the robot asserts a real plan/result (never a crash/no-op).
#[derive(Debug, Clone, Serialize, Default, PartialEq, Eq)]
pub struct AirgapRun {
/// `true` once a plan/run completed (dry-run plan counts as ran).
pub ran: bool,
pub dry_run: bool,
/// The pure-Rust plan text (always present once validated).
pub plan: String,
/// The driver's captured log (empty for a dry-run).
pub log: String,
pub exit_code: i32,
pub success: bool,
pub status: String,
pub error: Option<String>,
}
/// The result of one Tool #4 Deploy (Skíðblaðnir) run. Reads the selected repo's
/// **sealed znippy bundle** over the core API (`archive_info`) — the real artifact
/// to deploy — and folds in an airgap-engine push-only deploy plan for the chosen
/// test target. Surfaced in the Tools#4 pane + `state_json` so the robot asserts a
/// real deploy ran (never a no-op / "being implemented" stub).
#[derive(Debug, Clone, Serialize, Default, PartialEq, Eq)]
pub struct DeployRun {
/// `true` once a deploy plan was produced over a real sealed bundle.
pub ran: bool,
pub repository: String,
/// `"holger"` / `"nexus"` / `"artifactory"` — the airgap target deployed to.
pub target: String,
/// File count of the sealed znippy bundle being deployed (from `archive_info`).
pub file_count: u64,
/// Total uncompressed bytes of the sealed bundle.
pub bytes: u64,
/// The backing archive path / repo name reported by the core.
pub archive_path: String,
/// The airgap-engine deploy plan (push-only carry of the sealed bundle).
pub plan: String,
pub status: String,
pub error: Option<String>,
}
/// Tool #4 Deploy (Skíðblaðnir) configuration — pick the test target the selected
/// repo's sealed znippy bundle is deployed to. `target` reuses the airgap engine's
/// [`holger_airgap::Target`] so the deploy rides the documented airgap path.
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct DeployConfig {
/// The deploy target (reuses the airgap engine's targets).
pub target: holger_airgap::Target,
/// One-line status shown in the pane (last run / running notice).
pub status: String,
/// The last completed deploy's result, if any.
pub result: Option<DeployRun>,
}
impl Default for DeployConfig {
fn default() -> Self {
Self {
target: holger_airgap::Target::Holger,
status: String::new(),
result: None,
}
}
}
impl DeployConfig {
pub fn state_json(&self) -> Value {
serde_json::to_value(self).unwrap_or(Value::Null)
}
}