Skip to main content

visi_core/core/
style.rs

1//! Per-cell formatting.
2
3use serde::{Deserialize, Serialize};
4
5/// Cell formatting style attributes (font color, background color, font styles, font family, font size).
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
7pub struct CellStyle {
8    /// Font color as Hex (e.g. "#FF0000" or "FF0000") or standard color name ("red", "blue", etc.)
9    pub font_color: Option<String>,
10    /// Background fill color as Hex or color name
11    pub bg_color: Option<String>,
12    /// Bold text style flag
13    pub bold: Option<bool>,
14    /// Italic text style flag
15    pub italic: Option<bool>,
16    /// Underline text style flag
17    pub underline: Option<bool>,
18    /// Font family name (e.g. "Arial", "Calibri", "Courier New")
19    pub font_family: Option<String>,
20    /// Font size in points (e.g. 11, 12, 14)
21    pub font_size: Option<u16>,
22    /// Excel number-format code (e.g. `m/d/yy`, `yyyy-mm-dd`).
23    ///
24    /// This is how a date cell remembers the notation it was written in: the
25    /// value stays a plain numeric serial, exactly as in Excel, and the format
26    /// governs only how it renders. See `core::date`.
27    pub num_format: Option<String>,
28}
29
30impl CellStyle {
31    /// A style with nothing set.
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    /// Whether no attribute is set. An empty style is stored as no style at
37    /// all rather than kept around.
38    pub fn is_empty(&self) -> bool {
39        self.font_color.is_none()
40            && self.bg_color.is_none()
41            && self.bold.is_none()
42            && self.italic.is_none()
43            && self.underline.is_none()
44            && self.font_family.is_none()
45            && self.font_size.is_none()
46            && self.num_format.is_none()
47    }
48
49    /// Overlays `other` onto this style, attribute by attribute.
50    ///
51    /// Only the attributes `other` actually sets are copied, so merging a
52    /// style that just sets `bold` leaves an existing font color alone.
53    pub fn merge(&mut self, other: &CellStyle) {
54        if other.font_color.is_some() {
55            self.font_color = other.font_color.clone();
56        }
57        if other.bg_color.is_some() {
58            self.bg_color = other.bg_color.clone();
59        }
60        if other.bold.is_some() {
61            self.bold = other.bold;
62        }
63        if other.italic.is_some() {
64            self.italic = other.italic;
65        }
66        if other.underline.is_some() {
67            self.underline = other.underline;
68        }
69        if other.font_family.is_some() {
70            self.font_family = other.font_family.clone();
71        }
72        if other.font_size.is_some() {
73            self.font_size = other.font_size;
74        }
75        if other.num_format.is_some() {
76            self.num_format = other.num_format.clone();
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_cell_style_is_empty() {
87        let style = CellStyle::new();
88        assert!(style.is_empty());
89
90        let style_bold = CellStyle {
91            bold: Some(true),
92            ..Default::default()
93        };
94        assert!(!style_bold.is_empty());
95    }
96
97    #[test]
98    fn test_cell_style_merge() {
99        let mut style1 = CellStyle {
100            font_color: Some("#FF0000".to_string()),
101            bold: Some(true),
102            ..Default::default()
103        };
104        let style2 = CellStyle {
105            bg_color: Some("#00FF00".to_string()),
106            font_color: Some("#0000FF".to_string()),
107            font_size: Some(14),
108            ..Default::default()
109        };
110
111        style1.merge(&style2);
112        assert_eq!(style1.font_color, Some("#0000FF".to_string()));
113        assert_eq!(style1.bg_color, Some("#00FF00".to_string()));
114        assert_eq!(style1.bold, Some(true));
115        assert_eq!(style1.font_size, Some(14));
116    }
117}