Skip to main content

systemprompt_logging/services/cli/
theme.rs

1use console::{style, Emoji, StyledObject};
2
3pub use super::types::{
4    ActionType, ColorType, EmphasisType, IconType, ItemStatus, MessageLevel, ModuleType,
5};
6
7#[derive(Debug, Copy, Clone)]
8pub struct BrandColors;
9
10impl BrandColors {
11    pub fn primary<D: std::fmt::Display>(text: D) -> StyledObject<D> {
12        style(text).color256(208)
13    }
14
15    pub fn primary_bold<D: std::fmt::Display>(text: D) -> StyledObject<D> {
16        style(text).color256(208).bold()
17    }
18
19    pub fn white_bold<D: std::fmt::Display>(text: D) -> StyledObject<D> {
20        style(text).white().bold()
21    }
22
23    pub fn white<D: std::fmt::Display>(text: D) -> StyledObject<D> {
24        style(text).white()
25    }
26
27    pub fn dim<D: std::fmt::Display>(text: D) -> StyledObject<D> {
28        style(text).dim()
29    }
30
31    pub fn highlight<D: std::fmt::Display>(text: D) -> StyledObject<D> {
32        style(text).cyan()
33    }
34
35    pub fn running<D: std::fmt::Display>(text: D) -> StyledObject<D> {
36        style(text).green()
37    }
38
39    pub fn stopped<D: std::fmt::Display>(text: D) -> StyledObject<D> {
40        style(text).red()
41    }
42
43    pub fn starting<D: std::fmt::Display>(text: D) -> StyledObject<D> {
44        style(text).yellow()
45    }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum ServiceStatus {
50    Running,
51    Stopped,
52    Starting,
53    Failed,
54    Unknown,
55}
56
57impl ServiceStatus {
58    pub const fn symbol(&self) -> &'static str {
59        match self {
60            Self::Running => "●",
61            Self::Stopped => "○",
62            Self::Starting => "◐",
63            Self::Failed => "✗",
64            Self::Unknown => "?",
65        }
66    }
67
68    pub const fn text(&self) -> &'static str {
69        match self {
70            Self::Running => "Running",
71            Self::Stopped => "Stopped",
72            Self::Starting => "Starting",
73            Self::Failed => "Failed",
74            Self::Unknown => "Unknown",
75        }
76    }
77}
78
79#[derive(Debug, Copy, Clone)]
80pub struct Icons;
81
82impl Icons {
83    pub const CHECKMARK: Emoji<'static, 'static> = Emoji("✓", "✓");
84    pub const WARNING: Emoji<'static, 'static> = Emoji("⚠", "!");
85    pub const ERROR: Emoji<'static, 'static> = Emoji("✗", "X");
86    pub const INFO: Emoji<'static, 'static> = Emoji("ℹ", "i");
87
88    pub const PACKAGE: Emoji<'static, 'static> = Emoji("📦", "[MOD]");
89    pub const SCHEMA: Emoji<'static, 'static> = Emoji("📄", "[SCHEMA]");
90    pub const SEED: Emoji<'static, 'static> = Emoji("🌱", "[SEED]");
91    pub const CONFIG: Emoji<'static, 'static> = Emoji("⚙", "[CONFIG]");
92
93    pub const ARROW: Emoji<'static, 'static> = Emoji("→", "->");
94    pub const UPDATE: Emoji<'static, 'static> = Emoji("🔄", "[UPDATE]");
95    pub const INSTALL: Emoji<'static, 'static> = Emoji("📥", "[INSTALL]");
96    pub const PAUSE: Emoji<'static, 'static> = Emoji("⏸", "[PAUSED]");
97
98    pub const fn for_module_type(module_type: ModuleType) -> Emoji<'static, 'static> {
99        match module_type {
100            ModuleType::Schema => Self::SCHEMA,
101            ModuleType::Seed => Self::SEED,
102            ModuleType::Module => Self::PACKAGE,
103            ModuleType::Configuration => Self::CONFIG,
104        }
105    }
106
107    pub const fn for_status(status: ItemStatus) -> Emoji<'static, 'static> {
108        match status {
109            ItemStatus::Valid | ItemStatus::Applied => Self::CHECKMARK,
110            ItemStatus::Missing | ItemStatus::Pending => Self::WARNING,
111            ItemStatus::Failed => Self::ERROR,
112            ItemStatus::Disabled => Self::PAUSE,
113        }
114    }
115
116    pub const fn for_message_level(level: MessageLevel) -> Emoji<'static, 'static> {
117        match level {
118            MessageLevel::Success => Self::CHECKMARK,
119            MessageLevel::Warning => Self::WARNING,
120            MessageLevel::Error => Self::ERROR,
121            MessageLevel::Info => Self::INFO,
122        }
123    }
124}
125
126#[derive(Debug, Copy, Clone)]
127pub struct Colors;
128
129impl Colors {
130    pub fn success<D: std::fmt::Display>(text: D) -> StyledObject<D> {
131        style(text).green()
132    }
133
134    pub fn warning<D: std::fmt::Display>(text: D) -> StyledObject<D> {
135        style(text).yellow()
136    }
137
138    pub fn error<D: std::fmt::Display>(text: D) -> StyledObject<D> {
139        style(text).red()
140    }
141
142    pub fn info<D: std::fmt::Display>(text: D) -> StyledObject<D> {
143        style(text).cyan()
144    }
145
146    pub fn highlight<D: std::fmt::Display>(text: D) -> StyledObject<D> {
147        style(text).bold().cyan()
148    }
149
150    pub fn dim<D: std::fmt::Display>(text: D) -> StyledObject<D> {
151        style(text).dim()
152    }
153
154    pub fn bold<D: std::fmt::Display>(text: D) -> StyledObject<D> {
155        style(text).bold()
156    }
157
158    pub fn underlined<D: std::fmt::Display>(text: D) -> StyledObject<D> {
159        style(text).bold().underlined()
160    }
161
162    pub fn for_status<D: std::fmt::Display>(text: D, status: ItemStatus) -> StyledObject<D> {
163        match status {
164            ItemStatus::Valid | ItemStatus::Applied => Self::success(text),
165            ItemStatus::Missing | ItemStatus::Pending => Self::warning(text),
166            ItemStatus::Failed => Self::error(text),
167            ItemStatus::Disabled => Self::dim(text),
168        }
169    }
170
171    pub fn for_message_level<D: std::fmt::Display>(
172        text: D,
173        level: MessageLevel,
174    ) -> StyledObject<D> {
175        match level {
176            MessageLevel::Success => Self::success(text),
177            MessageLevel::Warning => Self::warning(text),
178            MessageLevel::Error => Self::error(text),
179            MessageLevel::Info => Self::info(text),
180        }
181    }
182}
183
184#[derive(Debug, Clone, Copy)]
185pub struct Theme;
186
187impl Theme {
188    pub fn icon(icon_type: impl Into<IconType>) -> Emoji<'static, 'static> {
189        match icon_type.into() {
190            IconType::Status(status) => Icons::for_status(status),
191            IconType::Module(module_type) => Icons::for_module_type(module_type),
192            IconType::Message(level) => Icons::for_message_level(level),
193            IconType::Action(action) => match action {
194                ActionType::Install => Icons::INSTALL,
195                ActionType::Update => Icons::UPDATE,
196                ActionType::Arrow => Icons::ARROW,
197            },
198        }
199    }
200
201    pub fn color<D: std::fmt::Display>(
202        text: D,
203        color_type: impl Into<ColorType>,
204    ) -> StyledObject<D> {
205        match color_type.into() {
206            ColorType::Status(status) => Colors::for_status(text, status),
207            ColorType::Message(level) => Colors::for_message_level(text, level),
208            ColorType::Emphasis(emphasis) => match emphasis {
209                EmphasisType::Highlight => Colors::highlight(text),
210                EmphasisType::Dim => Colors::dim(text),
211                EmphasisType::Bold => Colors::bold(text),
212                EmphasisType::Underlined => Colors::underlined(text),
213            },
214        }
215    }
216}