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