1use crate::core::ToXml;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct RgbColor {
10 pub r: u8,
11 pub g: u8,
12 pub b: u8,
13}
14
15impl RgbColor {
16 pub fn new(r: u8, g: u8, b: u8) -> Self {
18 Self { r, g, b }
19 }
20
21 pub fn from_hex(hex: &str) -> Option<Self> {
23 let hex = hex.trim_start_matches('#');
24 if hex.len() != 6 {
25 return None;
26 }
27 let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
28 let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
29 let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
30 Some(Self { r, g, b })
31 }
32
33 pub fn to_hex(&self) -> String {
35 format!("{:02X}{:02X}{:02X}", self.r, self.g, self.b)
36 }
37
38 pub fn black() -> Self { Self::new(0, 0, 0) }
40 pub fn white() -> Self { Self::new(255, 255, 255) }
41 pub fn red() -> Self { Self::new(255, 0, 0) }
42 pub fn green() -> Self { Self::new(0, 255, 0) }
43 pub fn blue() -> Self { Self::new(0, 0, 255) }
44}
45
46impl ToXml for RgbColor {
47 fn to_xml(&self) -> String {
48 format!(r#"<a:srgbClr val="{}"/>"#, self.to_hex())
49 }
50}
51
52impl Default for RgbColor {
53 fn default() -> Self {
54 Self::black()
55 }
56}
57
58#[derive(Clone, Debug, PartialEq, Eq)]
60pub enum SchemeColor {
61 Accent1,
62 Accent2,
63 Accent3,
64 Accent4,
65 Accent5,
66 Accent6,
67 Dark1,
68 Dark2,
69 Light1,
70 Light2,
71 Hyperlink,
72 FollowedHyperlink,
73 Background1,
74 Background2,
75 Text1,
76 Text2,
77}
78
79impl SchemeColor {
80 pub fn as_str(&self) -> &'static str {
81 match self {
82 SchemeColor::Accent1 => "accent1",
83 SchemeColor::Accent2 => "accent2",
84 SchemeColor::Accent3 => "accent3",
85 SchemeColor::Accent4 => "accent4",
86 SchemeColor::Accent5 => "accent5",
87 SchemeColor::Accent6 => "accent6",
88 SchemeColor::Dark1 => "dk1",
89 SchemeColor::Dark2 => "dk2",
90 SchemeColor::Light1 => "lt1",
91 SchemeColor::Light2 => "lt2",
92 SchemeColor::Hyperlink => "hlink",
93 SchemeColor::FollowedHyperlink => "folHlink",
94 SchemeColor::Background1 => "bg1",
95 SchemeColor::Background2 => "bg2",
96 SchemeColor::Text1 => "tx1",
97 SchemeColor::Text2 => "tx2",
98 }
99 }
100}
101
102impl ToXml for SchemeColor {
103 fn to_xml(&self) -> String {
104 format!(r#"<a:schemeClr val="{}"/>"#, self.as_str())
105 }
106}
107
108#[derive(Clone, Debug, PartialEq)]
110pub enum Color {
111 Rgb(RgbColor),
112 Scheme(SchemeColor),
113}
114
115impl Color {
116 pub fn rgb(r: u8, g: u8, b: u8) -> Self {
118 Color::Rgb(RgbColor::new(r, g, b))
119 }
120
121 pub fn from_hex(hex: &str) -> Option<Self> {
123 RgbColor::from_hex(hex).map(Color::Rgb)
124 }
125
126 pub fn scheme(color: SchemeColor) -> Self {
128 Color::Scheme(color)
129 }
130}
131
132impl ToXml for Color {
133 fn to_xml(&self) -> String {
134 match self {
135 Color::Rgb(rgb) => rgb.to_xml(),
136 Color::Scheme(scheme) => scheme.to_xml(),
137 }
138 }
139}
140
141impl Default for Color {
142 fn default() -> Self {
143 Color::Rgb(RgbColor::black())
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_rgb_from_hex() {
153 let color = RgbColor::from_hex("FF0000").unwrap();
154 assert_eq!(color.r, 255);
155 assert_eq!(color.g, 0);
156 assert_eq!(color.b, 0);
157
158 let color = RgbColor::from_hex("#00FF00").unwrap();
159 assert_eq!(color.to_hex(), "00FF00");
160 }
161
162 #[test]
163 fn test_rgb_to_xml() {
164 let color = RgbColor::new(255, 0, 0);
165 assert_eq!(color.to_xml(), r#"<a:srgbClr val="FF0000"/>"#);
166 }
167
168 #[test]
169 fn test_scheme_color() {
170 let color = SchemeColor::Accent1;
171 assert_eq!(color.to_xml(), r#"<a:schemeClr val="accent1"/>"#);
172 }
173
174 #[test]
175 fn test_color_enum() {
176 let rgb = Color::rgb(255, 0, 0);
177 assert_eq!(rgb.to_xml(), r#"<a:srgbClr val="FF0000"/>"#);
178
179 let scheme = Color::scheme(SchemeColor::Dark1);
180 assert_eq!(scheme.to_xml(), r#"<a:schemeClr val="dk1"/>"#);
181 }
182
183 #[test]
184 fn test_common_colors() {
185 assert_eq!(RgbColor::black().to_hex(), "000000");
186 assert_eq!(RgbColor::white().to_hex(), "FFFFFF");
187 assert_eq!(RgbColor::red().to_hex(), "FF0000");
188 }
189}