Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/monetize-server/src/estate_order/standing.rs

//! **THE ESTATES THAT EXIST** (owner 2026-10-29, lane OVW-CLEAN) — what the console's Overview
//! lists, decided here and only rendered there (`Estates.Overview`).
//!
//! The box keeps two records of an estate: the fleet's (the guardian's store: an estate declared
//! at a join, at a sold estate's install, from `[products.<id>] instance`) and the shop's run (an
//! order, from `ordered` to `terminated`). Neither is ever forgotten by a terminate: the run is
//! kept as the estate's history and the fleet's estate row stays (only its boxes go). So every
//! estate this box ever laid or heard of has a record, and each record is classified ONCE, here
//! ([`standing`]):
//!
//! * **installed**: a box of it reported in (or this box laid one), or its run is delivered;
//! * **laying**: its run is ordered, paid, laying, installing, or owed (paid, waiting for the
//!   operator's retry or refund);
//! * **ended**: its run ended (terminated, refunded, released, deleted) and no box of it reports;
//! * **never installed**: no run, and no box of it ever reported in.
//!
//! Installed and laying are the Overview's rows; ended and never installed are its one quiet
//! line. A declared operator site with no row is the slot's to start by its name
//! ([`super::new_estate`]).

use std::collections::BTreeMap;

use super::{phase, EstateRun};
use crate::state::AppState;

/// **Where an estate stands**, as the Overview groups it.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Standing {
    Installed,
    Laying,
    Ended,
    NeverInstalled,
}

impl Standing {
    /// The wire's word.
    pub fn as_str(self) -> &'static str {
        match self {
            Standing::Installed => "installed",
            Standing::Laying => "laying",
            Standing::Ended => "ended",
            Standing::NeverInstalled => "never_installed",
        }
    }

    /// An Overview row (it exists); else the quiet line.
    pub fn is_row(self) -> bool {
        matches!(self, Standing::Installed | Standing::Laying)
    }
}

/// Pure: **the one classification** — `machines`: some box of the estate reported in at least
/// once, or this box laid one; `run`: the phase of its newest run, if it has one.
///
/// * A delivered run (`captured`, `grace`, `suspended`, `deleting`) or one being terminated
///   still stands: installed.
/// * A run on its way (`ordered` … `paired`) is laying, whatever reports. A setup that failed
///   (`failed`, `owed`) is laying too, unless boxes of it report: then something of it stands.
/// * An ended run (`terminated`, `refunded`, `released`, `deleted`) is ended, unless a box of it
///   reports AGAIN (a terminate forgets every box, so a report after it is a new life).
/// * No run: installed when a box reported, else never installed.
pub fn standing(machines: bool, run: Option<&str>) -> Standing {
    match run {
        Some(p) if phase::is_gone(p) => {
            if machines {
                Standing::Installed
            } else {
                Standing::Ended
            }
        }
        Some(p) if phase::is_term(p) || p == phase::TERMINATING => Standing::Installed,
        Some(p) if matches!(p, phase::FAILED | phase::OWED) && machines => Standing::Installed,
        Some(_) => Standing::Laying,
        None if machines => Standing::Installed,
        None => Standing::NeverInstalled,
    }
}

/// When `run` moved to its phase, if its history says.
fn since(run: &EstateRun) -> Option<u64> {
    run.history
        .iter()
        .rev()
        .find(|t| t.phase == run.phase)
        .map(|t| t.at_ms)
}

/// [`words_of`] for a record: its run's phase and when it moved there.
pub fn words(s: Standing, run: Option<&EstateRun>) -> String {
    words_of(
        s,
        run.map(|r| r.phase.as_str()).unwrap_or_default(),
        run.and_then(since),
    )
}

/// Pure: **the sentence for a standing**, from its run's phase (empty: no run) and when it
/// moved there.
pub fn words_of(s: Standing, p: &str, since_ms: Option<u64>) -> String {
    let on = since_ms
        .map(|ms| format!(" on {}", super::iso_date(ms)))
        .unwrap_or_default();
    match s {
        Standing::Installed => match p {
            phase::TERMINATING => "being terminated: its machines are given back".into(),
            phase::GRACE => "installed; its paid period ended, it runs in its grace".into(),
            phase::SUSPENDED => {
                "installed; suspended unpaid: its servers are stopped, its disks kept".into()
            }
            phase::DELETING => "installed; unpaid past its notice: being given back".into(),
            _ => "installed: its machines have reported in".into(),
        },
        Standing::Laying => match p {
            phase::ORDERED => "ordered: waiting for its payment".into(),
            phase::HELD => "paid: waiting to be laid".into(),
            phase::WAITING_DNS => "paid: waiting for its DNS records".into(),
            phase::LAYING => "paid: being laid".into(),
            phase::INSTALLING => "paid: installing".into(),
            phase::PAIRED => "paid: paired, finishing its install".into(),
            phase::FAILED => "paid: its setup failed and is tried again".into(),
            phase::OWED => {
                "paid, and it could not be set up: retry or refund it on the Sites page".into()
            }
            other => format!("being laid ({other})"),
        },
        Standing::Ended => match p {
            phase::TERMINATED => format!("terminated{on}"),
            phase::REFUNDED => format!("cancelled and refunded{on}"),
            phase::RELEASED => format!("never paid: its order was released{on}"),
            phase::DELETED => format!("unpaid: given back{on}"),
            other => format!("ended ({other}){on}"),
        },
        Standing::NeverInstalled => "never installed: no machine of it ever reported in".into(),
    }
}

/// **One estate as the Overview names it.**
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct EstateLine {
    pub name: String,
    pub product: String,
    pub standing: &'static str,
    pub words: String,
    /// The fleet (the guardian) knows it: its summary can be read.
    pub in_fleet: bool,
    pub domain: String,
}

/// **The Overview, decided**: its rows, its quiet line, and the slot's choices.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Overview {
    pub estates: Vec<EstateLine>,
    pub ended: Vec<EstateLine>,
    /// The products a new estate can be made of here ([`super::new_estate::template_of`]).
    pub products: Vec<String>,
    /// The operator's declared sites with no row: started from the slot by their name.
    pub declared: Vec<String>,
}

/// The run's product: its own, else its site's, else the shop's.
fn product_of(state: &AppState, run: &EstateRun) -> String {
    if !run.product.trim().is_empty() {
        return run.product.trim().to_string();
    }
    crate::fleet::run_product(state, run).unwrap_or_default()
}

/// Where a run's estate is reached: a site's own domain; a sold estate's own domain, else
/// `<company>.<its site>`.
fn domain_of(state: &AppState, run: &EstateRun) -> String {
    if run.site_order {
        return state.registry.site_of(&run.estate).unwrap_or_default();
    }
    if !run.domain.trim().is_empty() {
        return run.domain.trim().to_string();
    }
    state
        .registry
        .site_of(&run.estate)
        .map(|s| format!("{}.{s}", run.company))
        .unwrap_or_default()
}

/// **Every estate the box keeps a record of, classified** — the fleet's estates and the shop's
/// runs, one line per fleet name, by name.
pub fn overview(state: &AppState) -> Overview {
    let runs = state.estates.all();
    let mut lines: BTreeMap<String, EstateLine> = BTreeMap::new();
    // The fleet's estates, each with its run (a site's by its label, a sold estate's by its company).
    if let Some(g) = state.fleet.guardian() {
        for (instance, product) in g.store.estates() {
            let run = state.estates.get(&instance);
            let laid = run
                .as_ref()
                .and_then(|r| r.laid.as_ref())
                .is_some_and(|l| !l.machines.is_empty())
                && run.as_ref().is_some_and(|r| !phase::is_gone(&r.phase));
            // Every role with a key, and the three every estate names (a box record is kept by role).
            let mut roles = g.store.roles_of(&instance);
            roles.extend(["front", "main", "twin"].map(String::from));
            let machines = laid
                || roles.iter().any(|role| {
                    g.store
                        .box_record(&instance, role)
                        .is_some_and(|b| b.heard_unix_ms > 0)
                });
            let s = standing(machines, run.as_ref().map(|r| r.phase.as_str()));
            let domain = run
                .as_ref()
                .map(|r| domain_of(state, r))
                .filter(|d| !d.is_empty())
                .or_else(|| state.registry.site_of(&instance))
                .unwrap_or_default();
            lines.insert(
                instance.clone(),
                EstateLine {
                    name: instance,
                    product,
                    standing: s.as_str(),
                    words: words(s, run.as_ref()),
                    in_fleet: true,
                    domain,
                },
            );
        }
    }
    // The shop's runs the fleet does not know (yet): an order on its way, or one that ended
    // before its estate was installed. The newest run of a name is the one kept.
    for run in &runs {
        let name = run.fleet_name().to_string();
        if name.is_empty() || lines.contains_key(&name) {
            continue;
        }
        let s = standing(false, Some(&run.phase));
        lines.insert(
            name.clone(),
            EstateLine {
                name,
                product: product_of(state, run),
                standing: s.as_str(),
                words: words(s, Some(run)),
                in_fleet: false,
                domain: domain_of(state, run),
            },
        );
    }
    let (estates, ended): (Vec<EstateLine>, Vec<EstateLine>) = lines.into_values().partition(|l| {
        l.standing == Standing::Installed.as_str() || l.standing == Standing::Laying.as_str()
    });
    let rows: std::collections::BTreeSet<&str> = estates.iter().map(|l| l.name.as_str()).collect();
    let declared = state
        .registry
        .declared_estates()
        .into_iter()
        .filter(|(label, e)| e.operator_site && !rows.contains(label.as_str()))
        .map(|(label, _)| label)
        .collect();
    let products = super::new_estate::products_offered(state);
    Overview {
        estates,
        ended,
        products,
        declared,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// ★ **Every record is classified once** (OVW-CLEAN): the run's phase first, the machines
    /// that reported second.
    #[test]
    fn a_record_is_installed_laying_ended_or_never_installed() {
        use Standing::*;
        // No run: a box that reported is an install (a kit-installed site: gunnar.rs); none, never.
        assert_eq!(standing(true, None), Installed);
        assert_eq!(
            standing(false, None),
            NeverInstalled,
            "declared at a join or by [products] instance, nothing ever reported"
        );
        // On its way: ordered, paid, laying, whatever reports.
        for p in [
            phase::ORDERED,
            phase::HELD,
            phase::WAITING_DNS,
            phase::LAYING,
            phase::INSTALLING,
            phase::PAIRED,
        ] {
            assert_eq!(standing(false, Some(p)), Laying, "{p}");
            assert_eq!(
                standing(true, Some(p)),
                Laying,
                "{p}: a site laid again over its old boxes is being laid"
            );
        }
        // A setup that failed is the operator's to settle, unless something of it stands.
        for p in [phase::FAILED, phase::OWED] {
            assert_eq!(standing(false, Some(p)), Laying, "{p}");
            assert_eq!(standing(true, Some(p)), Installed, "{p}: its boxes report");
        }
        // Delivered, in its term, or being ended: it stands.
        for p in [
            phase::CAPTURED,
            phase::GRACE,
            phase::SUSPENDED,
            phase::DELETING,
            phase::TERMINATING,
        ] {
            assert_eq!(standing(false, Some(p)), Installed, "{p}");
        }
        // Ended: terminated (acme2, ignalina, polka, rickardtest, test3-holger-rs), refunded,
        // released, deleted — unless a box of it reports again.
        for p in [
            phase::TERMINATED,
            phase::REFUNDED,
            phase::RELEASED,
            phase::DELETED,
        ] {
            assert_eq!(standing(false, Some(p)), Ended, "{p}");
            assert_eq!(
                standing(true, Some(p)),
                Installed,
                "{p}: a box reports after it ended"
            );
        }
        assert!(
            Installed.is_row() && Laying.is_row() && !Ended.is_row() && !NeverInstalled.is_row()
        );
        assert_eq!(
            [Installed, Laying, Ended, NeverInstalled].map(Standing::as_str),
            ["installed", "laying", "ended", "never_installed"]
        );
    }

    /// ★ The sentence names the step it stands at, and an ended one when it ended.
    #[test]
    fn the_words_name_the_step_and_when_it_ended() {
        const OCT_27: Option<u64> = Some(1_793_059_200_000);
        assert_eq!(
            words_of(Standing::Laying, phase::ORDERED, None),
            "ordered: waiting for its payment"
        );
        assert_eq!(
            words_of(Standing::Laying, phase::LAYING, None),
            "paid: being laid"
        );
        assert!(words_of(Standing::Laying, phase::OWED, None).contains("retry or refund"));
        assert_eq!(
            words_of(Standing::Ended, phase::TERMINATED, OCT_27),
            "terminated on 2026-10-27"
        );
        assert_eq!(
            words_of(Standing::Ended, phase::REFUNDED, OCT_27),
            "cancelled and refunded on 2026-10-27"
        );
        assert_eq!(
            words_of(Standing::Ended, phase::TERMINATED, None),
            "terminated"
        );
        assert_eq!(
            words_of(Standing::NeverInstalled, "", None),
            "never installed: no machine of it ever reported in"
        );
        assert_eq!(
            words_of(Standing::Installed, "", None),
            "installed: its machines have reported in"
        );
    }
}