shimpz-cli 0.5.37

Fast local tooling for Shimpz Assistants
//! Concise, bounded Local Space status projection.

use super::state::Installed;

const SERVICE_COUNT: usize = 7;
const MAX_RECORD_BYTES: usize = 256;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Expectation {
    Service,
    Init,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct Component {
    docker_name: &'static str,
    service: &'static str,
    label: &'static str,
    expectation: Expectation,
}

pub(crate) const COMPONENTS: [Component; 8] = [
    Component {
        docker_name: "shimpz-admin",
        service: "admin",
        label: "Admin",
        expectation: Expectation::Service,
    },
    Component {
        docker_name: "shimpz-team",
        service: "team",
        label: "Team",
        expectation: Expectation::Service,
    },
    Component {
        docker_name: "shimpz-brain",
        service: "brain",
        label: "Brain",
        expectation: Expectation::Service,
    },
    Component {
        docker_name: "shimpz-brain-egress",
        service: "shimpz-brain-egress",
        label: "Brain network boundary",
        expectation: Expectation::Service,
    },
    Component {
        docker_name: "shimpz-assistant-egress",
        service: "shimpz-assistant-egress",
        label: "Assistant network boundary",
        expectation: Expectation::Service,
    },
    Component {
        docker_name: "shimpz-assistant-release",
        service: "shimpz-assistant-release",
        label: "Assistant release boundary",
        expectation: Expectation::Service,
    },
    Component {
        docker_name: "shimpz-account-egress",
        service: "shimpz-account-egress",
        label: "Account network boundary",
        expectation: Expectation::Service,
    },
    Component {
        docker_name: "shimpz-account-egress-init",
        service: "shimpz-account-egress-init",
        label: "Account setup",
        expectation: Expectation::Init,
    },
];

impl Component {
    pub(crate) const fn docker_name(self) -> &'static str {
        self.docker_name
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Health {
    Unavailable,
    Starting,
    Healthy,
    Unhealthy,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Runtime {
    Missing,
    Created,
    Running(Health),
    Paused,
    Restarting,
    Removing,
    Exited(i32),
    Dead(i32),
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct Observation {
    component: Component,
    runtime: Runtime,
}

impl Observation {
    pub(crate) const fn is_present(self) -> bool {
        !matches!(self.runtime, Runtime::Missing)
    }
}

pub(crate) fn not_installed() -> &'static str {
    "Shimpz Space is not installed.\nNext: shimpz install"
}

pub(crate) fn operation_in_progress() -> &'static str {
    "A Shimpz Space update is in progress.\nNext: run shimpz status again shortly."
}

pub(crate) fn observe(component: Component, record: Option<&str>) -> Result<Observation, String> {
    let Some(record) = record else {
        return Ok(Observation {
            component,
            runtime: Runtime::Missing,
        });
    };
    if record.len() > MAX_RECORD_BYTES || record.contains('\r') {
        return Err(malformed(component));
    }
    let mut lines = record.lines();
    let line = lines
        .next()
        .filter(|line| !line.is_empty() && lines.next().is_none())
        .ok_or_else(|| malformed(component))?;
    let fields: Vec<_> = line.split('|').collect();
    if fields.len() != 4 || fields[0] != component.service {
        return Err(malformed(component));
    }
    let health = match fields[2] {
        "" => Health::Unavailable,
        "starting" => Health::Starting,
        "healthy" => Health::Healthy,
        "unhealthy" => Health::Unhealthy,
        _ => return Err(malformed(component)),
    };
    let exit_code = fields[3]
        .parse::<i32>()
        .ok()
        .filter(|code| *code >= 0)
        .ok_or_else(|| malformed(component))?;
    let runtime = match fields[1] {
        "created" => Runtime::Created,
        "running" => Runtime::Running(health),
        "paused" => Runtime::Paused,
        "restarting" => Runtime::Restarting,
        "removing" => Runtime::Removing,
        "exited" => Runtime::Exited(exit_code),
        "dead" => Runtime::Dead(exit_code),
        _ => return Err(malformed(component)),
    };
    Ok(Observation { component, runtime })
}

pub(crate) fn render(
    installed: &Installed,
    graph_current: bool,
    observations: &[Observation],
) -> Result<String, String> {
    if observations.len() != COMPONENTS.len()
        || !observations
            .iter()
            .zip(COMPONENTS)
            .all(|(observation, component)| observation.component == component)
    {
        return Err("the Local runtime snapshot is incomplete".into());
    }
    let mut healthy = 0;
    let mut problems = Vec::new();
    if !graph_current {
        problems.push("Local configuration needs reconciliation.".to_owned());
    }
    for observation in observations {
        match observation.component.expectation {
            Expectation::Service => match service_problem(*observation) {
                Some(problem) => problems.push(problem),
                None => healthy += 1,
            },
            Expectation::Init => {
                if let Some(problem) = init_problem(*observation) {
                    problems.push(problem);
                }
            }
        }
    }
    if problems.is_empty() {
        return Ok(format!(
            "Shimpz Space is healthy.\nAdmin: http://127.0.0.1:{}\nServices: {healthy} healthy\nRelease: ordinal {}",
            installed.port, installed.ordinal
        ));
    }
    Ok(format!(
        "Shimpz Space needs attention.\nAdmin: http://127.0.0.1:{}\nServices: {healthy} of {SERVICE_COUNT} healthy\nRelease: ordinal {}\nProblems:\n  - {}\nNext: shimpz start",
        installed.port,
        installed.ordinal,
        problems.join("\n  - ")
    ))
}

fn service_problem(observation: Observation) -> Option<String> {
    let label = observation.component.label;
    Some(match observation.runtime {
        Runtime::Missing => format!("{label} is missing."),
        Runtime::Created => format!("{label} has not started."),
        Runtime::Running(Health::Unavailable) => format!("{label} health is unavailable."),
        Runtime::Running(Health::Starting) => format!("{label} is starting."),
        Runtime::Running(Health::Unhealthy) => format!("{label} is unhealthy."),
        Runtime::Paused => format!("{label} is paused."),
        Runtime::Restarting => format!("{label} is restarting."),
        Runtime::Removing => format!("{label} is being removed."),
        Runtime::Exited(code) => format!("{label} stopped with exit code {code}."),
        Runtime::Dead(code) => format!("{label} failed with exit code {code}."),
        Runtime::Running(Health::Healthy) => return None,
    })
}

fn init_problem(observation: Observation) -> Option<String> {
    Some(match observation.runtime {
        Runtime::Missing => "Account setup is missing.".into(),
        Runtime::Exited(0) => return None,
        Runtime::Exited(code) | Runtime::Dead(code) => {
            format!("Account setup failed with exit code {code}.")
        }
        Runtime::Created
        | Runtime::Running(_)
        | Runtime::Paused
        | Runtime::Restarting
        | Runtime::Removing => "Account setup is incomplete.".into(),
    })
}

fn malformed(component: Component) -> String {
    format!(
        "Docker returned malformed status for {}",
        component.docker_name
    )
}

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

    fn installed() -> Installed {
        Installed {
            space_id: "space-0123456789abcdef01234567".into(),
            release_ref: format!(
                "ghcr.io/theshimpz/shimpz-local-release@sha256:{}",
                "a".repeat(64)
            ),
            ordinal: 42,
            port: 7777,
        }
    }

    fn healthy_observations() -> Vec<Observation> {
        COMPONENTS
            .iter()
            .copied()
            .map(|component| {
                let record = if component.expectation == Expectation::Init {
                    format!("{}|exited||0", component.service)
                } else {
                    format!("{}|running|healthy|0", component.service)
                };
                observe(component, Some(&record)).unwrap()
            })
            .collect()
    }

    #[test]
    fn renders_only_actionable_healthy_details() {
        let rendered = render(&installed(), true, &healthy_observations()).unwrap();

        assert_eq!(
            rendered,
            "Shimpz Space is healthy.\nAdmin: http://127.0.0.1:7777\nServices: 7 healthy\nRelease: ordinal 42"
        );
        assert!(!rendered.contains("sha256:"));
        assert!(!rendered.contains("space-"));
    }

    #[test]
    fn covers_the_exact_static_resource_inventory() {
        let status_names = COMPONENTS
            .map(Component::docker_name)
            .into_iter()
            .collect::<std::collections::BTreeSet<_>>();
        let resource_names = super::super::resources::RESERVED
            .into_iter()
            .collect::<std::collections::BTreeSet<_>>();

        assert_eq!(status_names, resource_names);
        assert_eq!(
            COMPONENTS
                .iter()
                .filter(|component| component.expectation == Expectation::Service)
                .count(),
            SERVICE_COUNT
        );
    }

    #[test]
    fn renders_only_the_problems_in_a_degraded_space() {
        let mut observations = healthy_observations();
        observations[1] = observe(COMPONENTS[1], Some("team|exited||7")).unwrap();
        observations[5] = observe(
            COMPONENTS[5],
            Some("shimpz-assistant-release|running|unhealthy|0"),
        )
        .unwrap();
        observations[7] = observe(COMPONENTS[7], None).unwrap();

        let rendered = render(&installed(), false, &observations).unwrap();

        assert_eq!(
            rendered,
            "Shimpz Space needs attention.\nAdmin: http://127.0.0.1:7777\nServices: 5 of 7 healthy\nRelease: ordinal 42\nProblems:\n  - Local configuration needs reconciliation.\n  - Team stopped with exit code 7.\n  - Assistant release boundary is unhealthy.\n  - Account setup is missing.\nNext: shimpz start"
        );
    }

    #[test]
    fn maps_every_known_non_ready_runtime_to_static_copy() {
        let mut missing = healthy_observations();
        missing[0] = observe(COMPONENTS[0], None).unwrap();
        assert!(
            render(&installed(), true, &missing)
                .unwrap()
                .contains("  - Admin is missing.")
        );
        for record in [
            "admin|created||0",
            "admin|running||0",
            "admin|running|starting|0",
            "admin|paused|healthy|0",
            "admin|restarting|unhealthy|1",
            "admin|removing||0",
            "admin|dead||9",
        ] {
            let mut observations = healthy_observations();
            observations[0] = observe(COMPONENTS[0], Some(record)).unwrap();
            let rendered = render(&installed(), true, &observations).unwrap();
            assert!(rendered.starts_with("Shimpz Space needs attention."));
            assert!(rendered.contains("  - Admin"));
        }
        for record in [
            "shimpz-account-egress-init|running||0",
            "shimpz-account-egress-init|dead||0",
            "shimpz-account-egress-init|dead||2",
        ] {
            let mut observations = healthy_observations();
            observations[7] = observe(COMPONENTS[7], Some(record)).unwrap();
            assert!(
                render(&installed(), true, &observations)
                    .unwrap()
                    .contains("  - Account setup")
            );
        }
    }

    #[test]
    fn rejects_malformed_or_incomplete_snapshots() {
        let component = COMPONENTS[0];
        for record in [
            "",
            "admin|running|healthy",
            "team|running|healthy|0",
            "admin|unknown|healthy|0",
            "admin|running|unknown|0",
            "admin|running|healthy|-1",
            "admin|running|healthy|0\nadmin|running|healthy|0",
            "admin|running|healthy|0\r",
        ] {
            assert!(observe(component, Some(record)).is_err());
        }
        assert!(observe(component, Some(&"x".repeat(MAX_RECORD_BYTES + 1))).is_err());
        assert!(render(&installed(), true, &healthy_observations()[..7]).is_err());
        assert!(!observe(component, None).unwrap().is_present());
    }

    #[test]
    fn gives_clear_next_steps_for_non_runtime_states() {
        assert_eq!(
            not_installed(),
            "Shimpz Space is not installed.\nNext: shimpz install"
        );
        assert_eq!(
            operation_in_progress(),
            "A Shimpz Space update is in progress.\nNext: run shimpz status again shortly."
        );
    }
}