1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
5pub enum Tab {
6 #[default]
7 Status,
8 Jobs,
9 Models,
10 Config,
11 Logs,
12 About,
13}
14
15impl Tab {
16 pub const ALL: [Tab; 6] = [
17 Tab::Status,
18 Tab::Jobs,
19 Tab::Models,
20 Tab::Config,
21 Tab::Logs,
22 Tab::About,
23 ];
24
25 pub fn label(self) -> &'static str {
26 match self {
27 Tab::Status => "Status",
28 Tab::Jobs => "Jobs",
29 Tab::Models => "Models",
30 Tab::Config => "Config",
31 Tab::Logs => "Logs",
32 Tab::About => "About",
33 }
34 }
35
36 pub fn parse(name: &str) -> Option<Self> {
40 match name.trim().to_ascii_lowercase().as_str() {
41 "status" => Some(Self::Status),
42 "jobs" => Some(Self::Jobs),
43 "models" => Some(Self::Models),
44 "config" => Some(Self::Config),
45 "logs" => Some(Self::Logs),
46 "about" => Some(Self::About),
47 _ => None,
48 }
49 }
50
51 pub fn initial() -> Self {
53 std::env::var("STUDIO_WORKER_UI_TAB")
54 .ok()
55 .and_then(|s| Self::parse(&s))
56 .unwrap_or_default()
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn all_returns_every_tab_in_render_order() {
66 let labels: Vec<&str> = Tab::ALL.iter().map(|t| t.label()).collect();
67 assert_eq!(
68 labels,
69 ["Status", "Jobs", "Models", "Config", "Logs", "About"],
70 "tab labels + order are part of the UI contract"
71 );
72 }
73
74 #[test]
75 fn default_is_status() {
76 assert_eq!(Tab::default(), Tab::Status);
77 }
78
79 #[test]
80 fn parse_round_trips_with_label_case_insensitively() {
81 for tab in Tab::ALL {
82 assert_eq!(Tab::parse(tab.label()), Some(tab));
83 assert_eq!(Tab::parse(&tab.label().to_uppercase()), Some(tab));
84 }
85 assert!(Tab::parse("").is_none());
86 assert!(Tab::parse("nope").is_none());
87 }
88
89 #[test]
90 fn initial_falls_back_to_default_when_env_unset() {
91 let prev = std::env::var("STUDIO_WORKER_UI_TAB").ok();
97 std::env::remove_var("STUDIO_WORKER_UI_TAB");
98 assert_eq!(Tab::initial(), Tab::default());
99 if let Some(v) = prev {
100 std::env::set_var("STUDIO_WORKER_UI_TAB", v);
101 }
102 }
103
104 #[test]
105 fn labels_are_unique() {
106 use std::collections::HashSet;
107 let unique: HashSet<&str> = Tab::ALL.iter().map(|t| t.label()).collect();
108 assert_eq!(unique.len(), Tab::ALL.len());
109 }
110}