1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! # Modifier
//!
//! All of the things that can be done to a cell.  For the most part this comprises visual things
//! like fonts, formatting, alignment, etc but there are a couple tricky things here like `var`
//! which allows the user to bind variables to cells.
//!
mod border_side;
mod border_style;
mod horizontal_align;
mod number_format;
mod text_format;
mod vertical_align;

use crate::{Expand, Rgb};
pub use border_side::BorderSide;
pub use border_style::BorderStyle;
pub use horizontal_align::HorizontalAlign;
pub use number_format::NumberFormat;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
pub use text_format::TextFormat;
pub use vertical_align::VerticalAlign;

/// All of the traits that can be set on a cell modifier
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct Modifier {
    pub border_color: Option<Rgb>,
    pub border_style: Option<BorderStyle>,
    pub borders: HashSet<BorderSide>,
    pub color: Option<Rgb>,
    pub font_color: Option<Rgb>,
    pub font_family: Option<String>,
    pub font_size: Option<u8>,
    pub formats: HashSet<TextFormat>,
    pub horizontal_align: Option<HorizontalAlign>,
    pub lock: bool,
    pub note: Option<String>,
    pub number_format: Option<NumberFormat>,
    pub var: Option<String>,
    pub vertical_align: Option<VerticalAlign>,
}

/// Verrrry similar to `Modifier`, except `RowModifier` can also have an `expand`.
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct RowModifier {
    pub border_color: Option<Rgb>,
    pub border_style: Option<BorderStyle>,
    pub borders: HashSet<BorderSide>,
    pub color: Option<Rgb>,
    pub expand: Option<Expand>,
    pub font_color: Option<Rgb>,
    pub font_family: Option<String>,
    pub font_size: Option<u8>,
    pub formats: HashSet<TextFormat>,
    pub horizontal_align: Option<HorizontalAlign>,
    pub lock: bool,
    pub note: Option<String>,
    pub number_format: Option<NumberFormat>,
    pub var: Option<String>,
    pub vertical_align: Option<VerticalAlign>,
}

#[allow(clippy::from_over_into)]
impl Into<Modifier> for RowModifier {
    fn into(self) -> Modifier {
        Modifier {
            border_color: self.border_color,
            border_style: self.border_style,
            borders: self.borders.clone(),
            color: self.color,
            font_color: self.font_color,
            font_family: self.font_family,
            font_size: self.font_size,
            formats: self.formats.clone(),
            horizontal_align: self.horizontal_align,
            lock: self.lock,
            note: self.note,
            number_format: self.number_format,
            var: self.var,
            vertical_align: self.vertical_align,
        }
    }
}

impl Modifier {
    /// With the exception of `row_level` - has anything been set on this Modifier?
    pub fn is_empty(&self) -> bool {
        // I wish this wasn't so error prone
        self.border_color.is_none()
            && self.border_style.is_none()
            && self.borders.is_empty()
            && self.color.is_none()
            && self.font_color.is_none()
            && self.font_family.is_none()
            && self.font_size.is_none()
            && self.formats.is_empty()
            && self.horizontal_align.is_none()
            && !self.lock
            && self.note.is_none()
            && self.number_format.is_none()
            && self.var.is_none()
            && self.vertical_align.is_none()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_empty_true() {
        assert!(Modifier::default().is_empty());
    }

    #[test]
    fn is_empty_false() {
        let modifier = Modifier {
            note: Some("this is a note".to_string()),
            font_size: Some(12),
            vertical_align: Some(VerticalAlign::Top),

            ..Default::default()
        };

        assert!(!modifier.is_empty());
    }
}