backup_suite/ui/
colors.rs

1/// カラースキーム管理モジュール
2///
3/// ダーク/ライトモード対応、ステータス別カラーコード、アクセシビリティ対応
4///
5use console::Style;
6
7/// カラースキームモード
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum ColorScheme {
10    Dark,
11    Light,
12    Auto,
13    HighContrast,
14}
15
16impl ColorScheme {
17    /// 環境変数から自動検出
18    #[must_use]
19    pub fn detect() -> Self {
20        if let Ok(term) = std::env::var("TERM") {
21            if term.contains("256color") {
22                return Self::Auto;
23            }
24        }
25
26        // NO_COLORが設定されている場合はハイコントラスト
27        if std::env::var("NO_COLOR").is_ok() {
28            return Self::HighContrast;
29        }
30
31        Self::Auto
32    }
33}
34
35/// カラーテーマ
36pub struct ColorTheme {
37    scheme: ColorScheme,
38}
39
40impl ColorTheme {
41    #[must_use]
42    pub fn new(scheme: ColorScheme) -> Self {
43        Self { scheme }
44    }
45
46    #[must_use]
47    pub fn auto() -> Self {
48        Self::new(ColorScheme::detect())
49    }
50
51    /// --no-color フラグに基づいてテーマを作成
52    #[must_use]
53    pub fn from_no_color(no_color: bool) -> Self {
54        if no_color {
55            Self::new(ColorScheme::HighContrast)
56        } else {
57            Self::auto()
58        }
59    }
60
61    /// 成功メッセージ用スタイル(緑 - 落ち着いた色調)
62    #[must_use]
63    pub fn success(&self) -> Style {
64        match self.scheme {
65            ColorScheme::Dark | ColorScheme::Auto => Style::new().green(),
66            ColorScheme::Light => Style::new().green(),
67            ColorScheme::HighContrast => Style::new().bold(),
68        }
69    }
70
71    /// エラーメッセージ用スタイル(赤 - 落ち着いた色調)
72    #[must_use]
73    pub fn error(&self) -> Style {
74        match self.scheme {
75            ColorScheme::Dark | ColorScheme::Auto => Style::new().red(),
76            ColorScheme::Light => Style::new().red(),
77            ColorScheme::HighContrast => Style::new().bold(),
78        }
79    }
80
81    /// 警告メッセージ用スタイル(黄 - 落ち着いた色調)
82    #[must_use]
83    pub fn warning(&self) -> Style {
84        match self.scheme {
85            ColorScheme::Dark | ColorScheme::Auto => Style::new().yellow(),
86            ColorScheme::Light => Style::new().yellow(),
87            ColorScheme::HighContrast => Style::new().bold(),
88        }
89    }
90
91    /// 情報メッセージ用スタイル(青 - 落ち着いた色調)
92    #[must_use]
93    pub fn info(&self) -> Style {
94        match self.scheme {
95            ColorScheme::Dark | ColorScheme::Auto => Style::new().blue(),
96            ColorScheme::Light => Style::new().blue(),
97            ColorScheme::HighContrast => Style::new(),
98        }
99    }
100
101    /// 見出し用スタイル(シンプル)
102    #[must_use]
103    pub fn header(&self) -> Style {
104        match self.scheme {
105            ColorScheme::Dark | ColorScheme::Auto | ColorScheme::Light => Style::new().bold(),
106            ColorScheme::HighContrast => Style::new().bold(),
107        }
108    }
109
110    /// 強調表示用スタイル(シンプル)
111    #[must_use]
112    pub fn highlight(&self) -> Style {
113        Style::new().bold()
114    }
115
116    /// 通常テキスト用スタイル
117    #[must_use]
118    pub fn normal(&self) -> Style {
119        Style::new()
120    }
121
122    /// 薄い色のテキスト(補助情報用)
123    #[must_use]
124    pub fn dim(&self) -> Style {
125        match self.scheme {
126            ColorScheme::Dark => Style::new().dim(),
127            ColorScheme::Light => Style::new().dim(),
128            ColorScheme::Auto => Style::new().dim(),
129            ColorScheme::HighContrast => Style::new(),
130        }
131    }
132
133    /// 優先度別カラー
134    #[must_use]
135    pub fn priority_high(&self) -> Style {
136        self.error() // 赤系
137    }
138
139    #[must_use]
140    pub fn priority_medium(&self) -> Style {
141        self.warning() // 黄系
142    }
143
144    #[must_use]
145    pub fn priority_low(&self) -> Style {
146        self.info() // 青系
147    }
148}
149
150/// ヘルパー関数: 成功メッセージ(絵文字なし・シンプル)
151#[must_use]
152pub fn success(msg: &str) -> String {
153    ColorTheme::auto()
154        .success()
155        .apply_to(format!("[OK] {msg}"))
156        .to_string()
157}
158
159/// ヘルパー関数: エラーメッセージ(絵文字なし・シンプル)
160#[must_use]
161pub fn error(msg: &str) -> String {
162    ColorTheme::auto()
163        .error()
164        .apply_to(format!("[ERROR] {msg}"))
165        .to_string()
166}
167
168/// ヘルパー関数: 警告メッセージ(絵文字なし・シンプル)
169#[must_use]
170pub fn warning(msg: &str) -> String {
171    ColorTheme::auto()
172        .warning()
173        .apply_to(format!("[WARN] {msg}"))
174        .to_string()
175}
176
177/// ヘルパー関数: 情報メッセージ(絵文字なし・シンプル)
178#[must_use]
179pub fn info(msg: &str) -> String {
180    ColorTheme::auto()
181        .info()
182        .apply_to(format!("[INFO] {msg}"))
183        .to_string()
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn test_color_scheme_detection() {
192        let scheme = ColorScheme::detect();
193        // 環境に依存するのでパニックしないことだけ確認
194        assert!(matches!(
195            scheme,
196            ColorScheme::Auto | ColorScheme::HighContrast
197        ));
198    }
199
200    #[test]
201    fn test_theme_styles() {
202        let theme = ColorTheme::new(ColorScheme::Dark);
203
204        // スタイルが適用可能であることを確認
205        let _ = theme.success().apply_to("test");
206        let _ = theme.error().apply_to("test");
207        let _ = theme.warning().apply_to("test");
208        let _ = theme.info().apply_to("test");
209    }
210
211    #[test]
212    fn test_helper_functions() {
213        // ヘルパー関数がパニックしないことを確認
214        let _ = success("Test success");
215        let _ = error("Test error");
216        let _ = warning("Test warning");
217        let _ = info("Test info");
218    }
219}