use shep_core::status::ProcStatus;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Role {
Meadow,
Butter,
Bark,
Ink3,
}
pub(crate) const fn role_of(status: ProcStatus) -> Role {
match status {
ProcStatus::Online => Role::Meadow,
ProcStatus::Starting | ProcStatus::WaitingRestart => Role::Butter,
ProcStatus::Errored => Role::Bark,
ProcStatus::Stopping | ProcStatus::Stopped => Role::Ink3,
}
}
pub(crate) const fn face(status: ProcStatus) -> &'static str {
match status {
ProcStatus::Online => "(o.o)",
ProcStatus::Starting => "(o~o)",
ProcStatus::WaitingRestart => "(>_<)",
ProcStatus::Stopping | ProcStatus::Stopped => "(-.-)",
ProcStatus::Errored => "(x.x)",
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Reported {
Live(ProcStatus),
Silent,
}
impl Reported {
pub(crate) const fn of(status: ProcStatus, handshook: Option<bool>) -> Self {
match (status, handshook) {
(ProcStatus::Online, Some(false)) => Self::Silent,
_ => Self::Live(status),
}
}
pub(crate) fn word(self) -> String {
match self {
Self::Live(status) => status.to_string(),
Self::Silent => "silent".to_string(),
}
}
pub(crate) const fn face(self) -> &'static str {
match self {
Self::Live(status) => face(status),
Self::Silent => "(?_?)",
}
}
pub(crate) const fn role(self) -> Role {
match self {
Self::Live(status) => role_of(status),
Self::Silent => Role::Butter,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_status_has_a_five_column_face() {
for status in [
ProcStatus::Online,
ProcStatus::Starting,
ProcStatus::WaitingRestart,
ProcStatus::Stopping,
ProcStatus::Stopped,
ProcStatus::Errored,
] {
let face = Reported::Live(status).face();
assert_eq!(
face.chars().count(),
5,
"{status} face {face:?} must be 5 columns; the table budget assumes it"
);
assert!(
face.is_ascii(),
"{status} face {face:?} must be ASCII: an emoji is \
double-width, inconsistently so, and cannot take a colour"
);
}
let silent = Reported::Silent.face();
assert_eq!(silent.chars().count(), 5, "silent face {silent:?}");
assert!(silent.is_ascii(), "silent face {silent:?}");
}
#[test]
fn a_silence_overrides_online_and_nothing_else() {
assert_eq!(
Reported::of(ProcStatus::Online, Some(false)),
Reported::Silent
);
for status in [
ProcStatus::Starting,
ProcStatus::Stopping,
ProcStatus::Stopped,
ProcStatus::Errored,
ProcStatus::WaitingRestart,
] {
assert_eq!(Reported::of(status, Some(false)), Reported::Live(status));
}
assert_eq!(
Reported::of(ProcStatus::Online, Some(true)),
Reported::Live(ProcStatus::Online)
);
assert_eq!(
Reported::of(ProcStatus::Online, None),
Reported::Live(ProcStatus::Online)
);
}
#[test]
fn a_silent_row_says_silent_in_butter() {
assert_eq!(Reported::Silent.word(), "silent");
assert_eq!(Reported::Silent.role(), Role::Butter);
assert_eq!(
Reported::Live(ProcStatus::Online).word(),
ProcStatus::Online.to_string(),
"a live row still spells its status exactly as the wire does"
);
}
#[test]
fn the_roles_match_what_lookout_already_showed() {
assert_eq!(role_of(ProcStatus::Online), Role::Meadow);
assert_eq!(role_of(ProcStatus::Starting), Role::Butter);
assert_eq!(role_of(ProcStatus::WaitingRestart), Role::Butter);
assert_eq!(role_of(ProcStatus::Errored), Role::Bark);
assert_eq!(role_of(ProcStatus::Stopping), Role::Ink3);
assert_eq!(role_of(ProcStatus::Stopped), Role::Ink3);
}
#[test]
fn the_faces_are_distinct_from_one_another() {
let faces = [
face(ProcStatus::Online),
face(ProcStatus::Starting),
face(ProcStatus::WaitingRestart),
face(ProcStatus::Stopped),
face(ProcStatus::Errored),
Reported::Silent.face(),
];
let mut seen = faces.to_vec();
seen.sort_unstable();
seen.dedup();
assert_eq!(
seen.len(),
faces.len(),
"each state needs its own face: {faces:?}"
);
}
}