crates/installer/nordisk-pairing/src/autofill.rs
//! **A re-laid main is filled from its twin by the FRONT** (Gunnar c7c9e11a, 2026-10-12;
//! shared here by lane G11, 2026-10-21, so Gunnar's and Holger's fronts decide it alike).
//!
//! A main laid again (its server gone, or its data emptied) is re-admitted fresh and holds
//! itself back: it AWAITS its twin (Gunnar `Bootstrap.Status.recharge_from`, Holger
//! `RechargeStatus.awaiting_from`). The front is the one place that reaches both boxes over
//! the tunnel with a key both accept (the console's), so when the estate opted into
//! auto-fix (the front's feed says `repair_auto = true`) the front does the operator's
//! [Recharge from twin] by itself: the product's ONE recharge path, into a main that SAYS
//! it awaits. A box that holds data is never streamed into.
//!
//! What is shared is the decision ([`due`]) and the words; each front asks its own boxes
//! (Gunnar: `gunnar_repair::recharge_from_twin`; Holger: `nordisk_twin::recharge::run`) and
//! runs the look as its own background job.
use std::time::Duration;
/// The fed opt-in: the front's feed carries `repair_auto = true` (exactly that word).
pub const FEED_REPAIR_AUTO: &str = "repair_auto";
/// How often a front looks (Gunnar's 20 s).
pub const LOOK_EVERY: Duration = Duration::from_secs(20);
/// **What the main says of itself**, in neither product's types.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Awaits<'a> {
/// It holds nothing of its own (a recharge may start).
pub fresh: bool,
/// A recharge began and is not whole (the product's recharge resumes it).
pub restoring: bool,
/// The twin its re-admission named, empty when none did (a first install).
pub recharge_from: &'a str,
}
/// Pure: **is auto-fix fed?** Only the exact word `true` (trimmed) says yes.
pub fn auto_on(auto: Option<&str>) -> bool {
auto.map(str::trim) == Some("true")
}
/// Pure: **may the front fill the main now?** Auto-fix fed exactly `true`, and the main
/// SAYS it awaits its twin: fresh (or a restore it began) and naming the twin.
pub fn due(auto: Option<&str>, main: &Awaits<'_>) -> bool {
auto_on(auto) && !main.recharge_from.trim().is_empty() && (main.fresh || main.restoring)
}
#[cfg(test)]
mod tests {
use super::*;
const TWIN: &str = "10.44.0.3:50051";
/// Gunnar's five cases (gunnar-front `autofill::due`, c7c9e11a): the front fills only a
/// main that SAYS it awaits its twin, and only with the opt-in fed.
#[test]
fn the_front_fills_only_an_awaiting_main_with_auto_fix_fed() {
let awaiting = Awaits { fresh: true, restoring: false, recharge_from: TWIN };
assert!(due(Some("true"), &awaiting));
assert!(due(Some(" true\n"), &awaiting), "the word is trimmed");
assert!(!due(None, &awaiting) && !due(Some("yes"), &awaiting) && !due(Some("TRUE"), &awaiting), "auto-fix not fed");
let restoring = Awaits { fresh: false, restoring: true, ..awaiting };
assert!(due(Some("true"), &restoring), "a restore it began is resumed");
let whole = Awaits { fresh: false, restoring: false, ..awaiting };
assert!(!due(Some("true"), &whole), "a main with data is never streamed into");
let first_install = Awaits { fresh: true, restoring: false, recharge_from: " " };
assert!(!due(Some("true"), &first_install), "a fresh main nobody re-admitted as a recharge is not filled");
}
}