xtask/src/variant_svg.rs
//! `cargo xtask svg` — **one picture per installer variant**: `solo`,
//! `gunnar-twin`, `gunnar-twin-one` (`.nornir/installer-build-thing-variants.md`).
//! There is exactly ONE monetize, the ONE box (owner 2026-10-12): it is drawn OUTSIDE
//! the estate, never on its front.
//!
//! A Build Thing variant carries an SVG of the system it installs. Each picture
//! here is a crop of ONE model, drawn by `setup_svg`'s renderer under its
//! layout guards: the machines (`vm1`, `vm2`, `vm3`), the one medium they boot,
//! the door they join, and the ONE monetize the boxes pull from.
//!
//! Two facts are pinned by test rather than left to the eye: main and twin boot
//! the SAME medium (ruling of 2026-09-21), and both of them JOIN the door (the
//! installer pairs them; the laptop does not).
//!
//! `where = kvm | upcloud` is a setting on every variant, not a picture: the
//! same install on both, so the same drawing.
use crate::setup_svg::{Kind, Model, Role, Route, Scene, Unit, Wire};
/// The installer's plane.
pub const W: f64 = 1040.0;
pub const H: f64 = 560.0;
pub const UNITS: &[Unit] = &[
Unit {
id: "front",
role: Role::Front,
kicker: Some("vm1"),
title: "front",
lines: &[
"door :8474 \u{b7} mutual TLS \u{b7} elects the twin",
"relay :51820 \u{b7} clock :50123 \u{b7} gunnar-ui",
],
x: 320.0,
y: 90.0,
w: 680.0,
h: 90.0,
},
Unit {
id: "one",
role: Role::Money,
kicker: Some("outside the estate"),
title: "the ONE monetize",
lines: &[
"monetize.ignalina.dk",
"the products are shown here",
"it dials no estate",
],
x: 24.0,
y: 90.0,
w: 250.0,
h: 100.0,
},
Unit {
id: "main",
role: Role::Main,
kicker: Some("vm2"),
title: "main",
lines: &[
"writable \u{2014} clients push here",
"git-ssh :2222 \u{b7} control :50051",
"primary, by the door's vote",
],
x: 320.0,
y: 290.0,
w: 280.0,
h: 108.0,
},
// Drawn only in `solo`, where it is the one machine: vm1, in main's place.
Unit {
id: "solo",
role: Role::Main,
kicker: Some("vm1"),
title: "gunnar",
lines: &[
"writable \u{2014} clients push here",
"git-ssh :2222 \u{b7} control :50051",
"no door, no twin, no money",
],
x: 320.0,
y: 290.0,
w: 280.0,
h: 108.0,
},
Unit {
id: "twin",
role: Role::Twin,
kicker: Some("vm3"),
title: "twin",
lines: &[
"the SAME medium as main",
"follows the primary's log",
"the weaker box, by the door",
],
x: 720.0,
y: 290.0,
w: 280.0,
h: 108.0,
},
Unit {
id: "medium",
role: Role::Client,
kicker: Some("the installer"),
title: "one medium",
lines: &[
"the door's address + the installer CA",
"the SAME bytes on every box",
],
x: 320.0,
y: 452.0,
w: 680.0,
h: 78.0,
},
// `solo`'s medium: the same installer with no seed — there is no door to join.
Unit {
id: "solo-medium",
role: Role::Client,
kicker: Some("the installer"),
title: "one medium",
lines: &[
"gunnar-installer, no pairing seed",
"nothing to join: the box is the estate",
],
x: 320.0,
y: 452.0,
w: 680.0,
h: 78.0,
},
];
pub const WIRES: &[Wire] = &[
Wire {
from: "solo-medium",
to: "solo",
route: Route::Rise { dx: 200.0 },
kind: Kind::Flow,
label: "boots",
note: "",
},
Wire {
from: "medium",
to: "main",
route: Route::Rise { dx: 200.0 },
kind: Kind::Flow,
label: "boots",
note: "",
},
Wire {
from: "medium",
to: "twin",
route: Route::Rise { dx: 600.0 },
kind: Kind::Flow,
label: "boots",
note: "the same bytes",
},
Wire {
from: "main",
to: "front",
route: Route::Rise { dx: 60.0 },
kind: Kind::Flow,
label: "join :8474",
note: "mTLS",
},
Wire {
from: "twin",
to: "front",
route: Route::Rise { dx: 60.0 },
kind: Kind::Flow,
label: "join :8474",
note: "mTLS",
},
Wire {
from: "main",
to: "twin",
route: Route::Across { dy: 30.0 },
kind: Kind::Flow,
label: "follows",
note: "in the tunnel",
},
Wire {
from: "one",
to: "main",
route: Route::Elbow { dx: 110.0 },
kind: Kind::Flow,
label: "orders",
note: "pulled by the boxes",
},
];
pub const VARIANTS: Model = Model {
units: UNITS,
wires: WIRES,
};
const WHERE: &str = "where = kvm | upcloud \u{2014} the same install, so the same picture";
pub const SCENES: &[Scene] = &[
Scene {
model: &VARIANTS,
asset: ".nornir/assets/solo.svg",
title: "solo \u{b7} one gunnar box",
subtitle: "one medium, one machine: no door, no twin, no monetize",
units: &["solo-medium", "solo"],
omit: &[],
footer: &[WHERE],
},
Scene {
model: &VARIANTS,
asset: ".nornir/assets/gunnar-twin.svg",
title: "gunnar-twin \u{b7} main + twin, paired by the door",
subtitle: "both boxes boot the same medium and join the front's door; the door elects the twin",
units: &["front", "main", "twin", "medium"],
omit: &[],
footer: &["open: does gunnar-twin keep vm1 for the door, or does the door move onto a box?", WHERE],
},
Scene {
model: &VARIANTS,
asset: ".nornir/assets/gunnar-twin-one.svg",
title: "gunnar-twin-one \u{b7} the estate books in the ONE monetize",
subtitle: "gunnar-twin with metered boxes; the front runs no monetize, the boxes pull from the ONE box",
units: &["front", "one", "main", "twin", "medium"],
omit: &[],
footer: &["enrolled first: `cargo xtask attach enrol` (private-gunnar-ops)", WHERE],
},
];
#[cfg(test)]
mod tests {
use super::*;
use crate::setup_svg::render_scene;
use crate::setup_svg::tests::{assert_scene_layout, assert_units_fit};
#[test]
fn the_model_closes() {
let mut seen = std::collections::BTreeSet::new();
for u in UNITS {
assert!(
seen.insert(u.id),
"variant_svg: two units called {:?}",
u.id
);
assert!(
u.x >= 0.0 && u.right() <= W && u.y >= 0.0 && u.bottom() <= H,
"variant_svg: {} is off the plane",
u.id
);
}
for w in WIRES {
assert!(
seen.contains(w.from) && seen.contains(w.to),
"variant_svg: a wire names no unit: {} â {}",
w.from,
w.to
);
}
assert_units_fit(UNITS);
}
/// One picture per variant the owner named, no more, no fewer.
#[test]
fn one_picture_per_variant() {
let names: Vec<&str> = SCENES
.iter()
.map(|s| {
s.asset
.trim_start_matches(".nornir/assets/")
.trim_end_matches(".svg")
})
.collect();
// Owner 2026-10-12: there is exactly ONE monetize, the ONE box — no variant puts a
// monetize on the estate's front; the ONE variant draws it OUTSIDE the estate.
assert_eq!(names, ["solo", "gunnar-twin", "gunnar-twin-one"]);
assert!(
!SCENES
.iter()
.any(|s| s.has("monetize") || s.has("provided")),
"a monetize drawn on the estate"
);
for u in UNITS {
assert!(
SCENES.iter().any(|s| s.has(u.id)),
"variant_svg: unit {:?} is in no picture",
u.id
);
}
}
/// Main and twin boot the SAME medium, and both JOIN the door â in every
/// picture that draws a twin. A picture where the laptop pairs them, or the
/// twin has a medium of its own, is the design this module exists to refuse.
#[test]
fn the_installer_pairs_both_boxes_from_one_medium() {
for sc in SCENES.iter().filter(|s| s.has("twin")) {
let wires = sc.wires();
for b in ["main", "twin"] {
assert!(
wires.iter().any(|w| w.from == "medium" && w.to == b),
"{}: {b} does not boot the medium",
sc.asset
);
assert!(
wires.iter().any(|w| w.from == b && w.to == "front"),
"{}: {b} does not join the door",
sc.asset
);
}
}
}
#[test]
fn nothing_is_drawn_on_top_of_anything() {
for sc in SCENES {
assert_scene_layout(sc);
}
}
#[test]
fn every_box_is_drawn() {
for sc in SCENES {
let svg = render_scene(sc);
for u in sc.units() {
assert!(
svg.contains(&crate::cycle_svg::esc(u.title)),
"{}: {} is not drawn",
sc.asset,
u.id
);
}
}
}
#[test]
fn the_committed_asset_is_fresh() {
for sc in SCENES {
crate::cycle_svg::assert_asset_fresh(
&crate::guard::workspace_root(),
sc.asset,
&render_scene(sc),
"cargo xtask svg",
);
}
}
}