Extract derive_run_state into format.rs
The identical state-derivation logic (outcome + dispatched_at → display
string) was duplicated in RunRow::derived_state(), RunListRow::state(),
and DetailRun::state(). Pull it into a single free function in format.rs
and have both template structs delegate to it. Remove the now-unused
RunRow::derived_state().
https://claude.ai/code/session_0171wuJy2GRJZuj2oVYZ2iKe
diff --git a/quire-server/src/quire/web/db.rs b/quire-server/src/quire/web/db.rs
index c59e154..10e3251 100644
--- a/quire-server/src/quire/web/db.rs
+++ b/quire-server/src/quire/web/db.rs
@@ -13,16 +13,6 @@ pub struct RunRow {
pub resolved_at: Option<i64>,
}
-impl RunRow {
- pub fn derived_state(&self) -> &str {
- match &self.outcome {
- Some(o) if o.starts_with("failed") => "failed",
- Some(o) => o.as_str(),
- None if self.dispatched_at.is_some() => "active",
- None => "queued",
- }
- }
-}
/// Raw job row from the database.
pub struct JobRow {
diff --git a/quire-server/src/quire/web/format.rs b/quire-server/src/quire/web/format.rs
index e2ee112..c136dc2 100644
--- a/quire-server/src/quire/web/format.rs
+++ b/quire-server/src/quire/web/format.rs
@@ -77,10 +77,18 @@ fn format_ms_duration(ms: i64) -> String {
format!("{secs}s")
}
+/// Derive a display state string from outcome and dispatched_at.
+pub fn derive_run_state(outcome: Option<&str>, dispatched_at: Option<i64>) -> &'static str {
+ match outcome {
+ Some("succeeded") => "succeeded",
+ Some("superseded") => "superseded",
+ Some(_) => "failed",
+ None if dispatched_at.is_some() => "active",
+ None => "queued",
+ }
+}
+
/// Map a CI run/job state string to a CSS colour class.
-///
-/// Centralised here so `RunListRow`, `DetailRun`, and `DetailJob`
-/// don't each carry their own identical match.
pub fn state_class(state: &str) -> &'static str {
match state {
"succeeded" => "c-ok",
@@ -186,4 +194,15 @@ mod tests {
assert_eq!(state_class("active"), "c-muted");
assert_eq!(state_class(""), "c-muted");
}
+
+ #[test]
+ fn derive_run_state_covers_all_outcomes() {
+ assert_eq!(derive_run_state(Some("succeeded"), Some(1)), "succeeded");
+ assert_eq!(derive_run_state(Some("superseded"), Some(1)), "superseded");
+ assert_eq!(derive_run_state(Some("failed-pipeline"), Some(1)), "failed");
+ assert_eq!(derive_run_state(Some("failed-orphaned"), Some(1)), "failed");
+ assert_eq!(derive_run_state(Some("failed-internal"), Some(1)), "failed");
+ assert_eq!(derive_run_state(None, Some(1)), "active");
+ assert_eq!(derive_run_state(None, None), "queued");
+ }
}
diff --git a/quire-server/src/quire/web/templates.rs b/quire-server/src/quire/web/templates.rs
index 499667c..93e7f50 100644
--- a/quire-server/src/quire/web/templates.rs
+++ b/quire-server/src/quire/web/templates.rs
@@ -61,12 +61,7 @@ pub struct RunListRow {
impl RunListRow {
pub fn state(&self) -> &str {
- match &self.outcome {
- Some(o) if o.starts_with("failed") => "failed",
- Some(o) => o.as_str(),
- None if self.dispatched_at.is_some() => "active",
- None => "queued",
- }
+ format::derive_run_state(self.outcome.as_deref(), self.dispatched_at)
}
pub fn state_class(&self) -> &'static str {
@@ -123,12 +118,7 @@ pub struct DetailRun {
impl DetailRun {
pub fn state(&self) -> &str {
- match &self.outcome {
- Some(o) if o.starts_with("failed") => "failed",
- Some(o) => o.as_str(),
- None if self.dispatched_at.is_some() => "active",
- None => "queued",
- }
+ format::derive_run_state(self.outcome.as_deref(), self.dispatched_at)
}
pub fn state_class(&self) -> &'static str {