git_iris/theme/
style.rs

1//! Theme style type - combines color with text modifiers.
2
3use super::color::ThemeColor;
4
5/// A composed style with foreground, background, and modifiers.
6///
7/// This represents a complete text style that can be applied to UI elements.
8/// Adapters convert this to framework-specific styles (Ratatui Style, etc.).
9#[derive(Clone, Debug, Default, PartialEq)]
10#[allow(clippy::struct_excessive_bools)] // Text style modifiers are inherently boolean flags
11pub struct ThemeStyle {
12    /// Foreground (text) color.
13    pub fg: Option<ThemeColor>,
14    /// Background color.
15    pub bg: Option<ThemeColor>,
16    /// Bold text modifier.
17    pub bold: bool,
18    /// Italic text modifier.
19    pub italic: bool,
20    /// Underline text modifier.
21    pub underline: bool,
22    /// Dim/faint text modifier.
23    pub dim: bool,
24}
25
26impl ThemeStyle {
27    /// Create a new empty style.
28    #[must_use]
29    pub const fn new() -> Self {
30        Self {
31            fg: None,
32            bg: None,
33            bold: false,
34            italic: false,
35            underline: false,
36            dim: false,
37        }
38    }
39
40    /// Create a style with only a foreground color.
41    #[must_use]
42    pub const fn fg(color: ThemeColor) -> Self {
43        Self {
44            fg: Some(color),
45            bg: None,
46            bold: false,
47            italic: false,
48            underline: false,
49            dim: false,
50        }
51    }
52
53    /// Create a style with only a background color.
54    #[must_use]
55    pub const fn bg(color: ThemeColor) -> Self {
56        Self {
57            fg: None,
58            bg: Some(color),
59            bold: false,
60            italic: false,
61            underline: false,
62            dim: false,
63        }
64    }
65
66    /// Set the foreground color.
67    #[must_use]
68    pub const fn with_fg(mut self, color: ThemeColor) -> Self {
69        self.fg = Some(color);
70        self
71    }
72
73    /// Set the background color.
74    #[must_use]
75    pub const fn with_bg(mut self, color: ThemeColor) -> Self {
76        self.bg = Some(color);
77        self
78    }
79
80    /// Enable bold modifier.
81    #[must_use]
82    pub const fn bold(mut self) -> Self {
83        self.bold = true;
84        self
85    }
86
87    /// Enable italic modifier.
88    #[must_use]
89    pub const fn italic(mut self) -> Self {
90        self.italic = true;
91        self
92    }
93
94    /// Enable underline modifier.
95    #[must_use]
96    pub const fn underline(mut self) -> Self {
97        self.underline = true;
98        self
99    }
100
101    /// Enable dim modifier.
102    #[must_use]
103    pub const fn dim(mut self) -> Self {
104        self.dim = true;
105        self
106    }
107
108    /// Merge another style onto this one.
109    /// Values from `other` take precedence where set.
110    #[must_use]
111    pub fn merge(&self, other: &Self) -> Self {
112        Self {
113            fg: other.fg.or(self.fg),
114            bg: other.bg.or(self.bg),
115            bold: other.bold || self.bold,
116            italic: other.italic || self.italic,
117            underline: other.underline || self.underline,
118            dim: other.dim || self.dim,
119        }
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_style_builder() {
129        let style = ThemeStyle::fg(ThemeColor::new(255, 0, 0))
130            .with_bg(ThemeColor::new(0, 0, 0))
131            .bold();
132
133        assert_eq!(style.fg, Some(ThemeColor::new(255, 0, 0)));
134        assert_eq!(style.bg, Some(ThemeColor::new(0, 0, 0)));
135        assert!(style.bold);
136        assert!(!style.italic);
137    }
138
139    #[test]
140    fn test_style_merge() {
141        let base = ThemeStyle::fg(ThemeColor::new(255, 0, 0)).bold();
142        let overlay = ThemeStyle::bg(ThemeColor::new(0, 0, 0)).italic();
143
144        let merged = base.merge(&overlay);
145        assert_eq!(merged.fg, Some(ThemeColor::new(255, 0, 0)));
146        assert_eq!(merged.bg, Some(ThemeColor::new(0, 0, 0)));
147        assert!(merged.bold);
148        assert!(merged.italic);
149    }
150}