1#![allow(clippy::module_name_repetitions)]
2
3use std::{fs::File, io::BufReader, num::ParseIntError, path::Path};
4
5use serde::{Deserialize, Serialize};
6use tuirealm::props::Color;
7
8use crate::config::yaml_theme::{
9 YAMLTheme, YAMLThemeBright, YAMLThemeCursor, YAMLThemeNormal, YAMLThemePrimary,
10};
11
12use styles::ColorTermusic;
13
14pub mod styles;
15
16#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)]
19#[serde(default)] pub struct ThemeWrap {
21 pub style: styles::Styles,
22 #[serde(default = "ThemeColors::full_default")]
25 pub theme: ThemeColors,
26}
27
28impl ThemeWrap {
29 #[must_use]
30 pub fn get_color_from_theme(&self, color: ColorTermusic) -> Color {
31 let val = match color {
33 ColorTermusic::Reset => return Color::Reset,
34 ColorTermusic::Foreground => &self.theme.primary.foreground,
35 ColorTermusic::Background => &self.theme.primary.background,
36 ColorTermusic::Black => &self.theme.normal.black,
37 ColorTermusic::Red => &self.theme.normal.red,
38 ColorTermusic::Green => &self.theme.normal.green,
39 ColorTermusic::Yellow => &self.theme.normal.yellow,
40 ColorTermusic::Blue => &self.theme.normal.blue,
41 ColorTermusic::Magenta => &self.theme.normal.magenta,
42 ColorTermusic::Cyan => &self.theme.normal.cyan,
43 ColorTermusic::White => &self.theme.normal.white,
44 ColorTermusic::LightBlack => &self.theme.bright.black,
45 ColorTermusic::LightRed => &self.theme.bright.red,
46 ColorTermusic::LightGreen => &self.theme.bright.green,
47 ColorTermusic::LightYellow => &self.theme.bright.yellow,
48 ColorTermusic::LightBlue => &self.theme.bright.blue,
49 ColorTermusic::LightMagenta => &self.theme.bright.magenta,
50 ColorTermusic::LightCyan => &self.theme.bright.cyan,
51 ColorTermusic::LightWhite => &self.theme.bright.white,
52 };
53
54 val.resolve_color(color)
56 }
57
58 #[inline]
59 #[must_use]
60 pub fn library_foreground(&self) -> Color {
61 self.get_color_from_theme(self.style.library.foreground_color)
62 }
63
64 #[inline]
65 #[must_use]
66 pub fn library_background(&self) -> Color {
67 self.get_color_from_theme(self.style.library.background_color)
68 }
69
70 #[inline]
71 #[must_use]
72 pub fn library_highlight(&self) -> Color {
73 self.get_color_from_theme(self.style.library.highlight_color)
74 }
75
76 #[inline]
77 #[must_use]
78 pub fn library_border(&self) -> Color {
79 self.get_color_from_theme(self.style.library.border_color)
80 }
81
82 #[inline]
83 #[must_use]
84 pub fn playlist_foreground(&self) -> Color {
85 self.get_color_from_theme(self.style.playlist.foreground_color)
86 }
87
88 #[inline]
89 #[must_use]
90 pub fn playlist_background(&self) -> Color {
91 self.get_color_from_theme(self.style.playlist.background_color)
92 }
93
94 #[inline]
95 #[must_use]
96 pub fn playlist_highlight(&self) -> Color {
97 self.get_color_from_theme(self.style.playlist.highlight_color)
98 }
99
100 #[inline]
101 #[must_use]
102 pub fn playlist_border(&self) -> Color {
103 self.get_color_from_theme(self.style.playlist.border_color)
104 }
105
106 #[inline]
107 #[must_use]
108 pub fn progress_foreground(&self) -> Color {
109 self.get_color_from_theme(self.style.progress.foreground_color)
110 }
111
112 #[inline]
113 #[must_use]
114 pub fn progress_background(&self) -> Color {
115 self.get_color_from_theme(self.style.progress.background_color)
116 }
117
118 #[inline]
119 #[must_use]
120 pub fn progress_border(&self) -> Color {
121 self.get_color_from_theme(self.style.progress.border_color)
122 }
123
124 #[inline]
125 #[must_use]
126 pub fn lyric_foreground(&self) -> Color {
127 self.get_color_from_theme(self.style.lyric.foreground_color)
128 }
129
130 #[inline]
131 #[must_use]
132 pub fn lyric_background(&self) -> Color {
133 self.get_color_from_theme(self.style.lyric.background_color)
134 }
135
136 #[inline]
137 #[must_use]
138 pub fn lyric_border(&self) -> Color {
139 self.get_color_from_theme(self.style.lyric.border_color)
140 }
141
142 #[inline]
143 #[must_use]
144 pub fn important_popup_foreground(&self) -> Color {
145 self.get_color_from_theme(self.style.important_popup.foreground_color)
146 }
147
148 #[inline]
149 #[must_use]
150 pub fn important_popup_background(&self) -> Color {
151 self.get_color_from_theme(self.style.important_popup.background_color)
152 }
153
154 #[inline]
155 #[must_use]
156 pub fn important_popup_border(&self) -> Color {
157 self.get_color_from_theme(self.style.important_popup.border_color)
158 }
159
160 #[inline]
161 #[must_use]
162 pub fn fallback_foreground(&self) -> Color {
163 self.get_color_from_theme(self.style.fallback.foreground_color)
164 }
165
166 #[inline]
167 #[must_use]
168 pub fn fallback_background(&self) -> Color {
169 self.get_color_from_theme(self.style.fallback.background_color)
170 }
171
172 #[inline]
173 #[must_use]
174 pub fn fallback_highlight(&self) -> Color {
175 self.get_color_from_theme(self.style.fallback.highlight_color)
176 }
177
178 #[inline]
179 #[must_use]
180 pub fn fallback_border(&self) -> Color {
181 self.get_color_from_theme(self.style.fallback.border_color)
182 }
183}
184
185#[derive(Debug, Clone, PartialEq, thiserror::Error)]
187pub enum ThemeColorParseError {
188 #[error("Failed to parse hex color: {0}")]
189 HexParseError(#[from] ThemeColorHexParseError),
190 #[error("Failed to parse color, expected prefix \"#\" or \"0x\" and length 6 or \"native\"")]
191 UnknownValue(String),
192}
193
194#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
195#[serde(try_from = "String")]
196#[serde(into = "String")]
197pub enum ThemeColor {
198 Native,
200 Hex(ThemeColorHex),
202}
203
204impl ThemeColor {
205 #[must_use]
209 pub const fn new_hex(r: u8, g: u8, b: u8) -> Self {
210 Self::Hex(ThemeColorHex::new(r, g, b))
211 }
212
213 #[must_use]
215 pub const fn new_native() -> Self {
216 Self::Native
217 }
218
219 fn from_string(val: &str) -> Result<Self, ThemeColorParseError> {
221 if val == "native" {
222 return Ok(Self::Native);
223 }
224
225 let res = match ThemeColorHex::try_from(val) {
226 Ok(v) => v,
227 Err(err) => {
228 return Err(match err {
229 ThemeColorHexParseError::UnknownPrefix(_) => {
231 ThemeColorParseError::UnknownValue(val.to_string())
232 }
233 v => ThemeColorParseError::HexParseError(v),
234 });
235 }
236 };
237
238 Ok(Self::Hex(res))
239 }
240
241 #[allow(clippy::inherent_to_string)] fn to_string(self) -> String {
244 match self {
245 ThemeColor::Native => "native".to_string(),
246 ThemeColor::Hex(theme_color_hex) => theme_color_hex.to_hex(),
247 }
248 }
249
250 #[must_use]
252 pub fn resolve_color(&self, style: ColorTermusic) -> Color {
253 let hex = match self {
254 ThemeColor::Native => return style.into(),
255 ThemeColor::Hex(theme_color_hex) => theme_color_hex,
256 };
257
258 (*hex).into()
259 }
260}
261
262impl TryFrom<String> for ThemeColor {
263 type Error = ThemeColorParseError;
264
265 fn try_from(value: String) -> Result<Self, Self::Error> {
266 Self::from_string(&value)
267 }
268}
269
270impl TryFrom<&str> for ThemeColor {
271 type Error = ThemeColorParseError;
272
273 fn try_from(value: &str) -> Result<Self, Self::Error> {
274 Self::from_string(value)
275 }
276}
277
278impl From<ThemeColor> for String {
279 fn from(val: ThemeColor) -> Self {
280 ThemeColor::to_string(val)
281 }
282}
283
284#[derive(Debug, Clone, PartialEq, thiserror::Error)]
286pub enum ThemeColorHexParseError {
287 #[error("Failed to parse color because of {0}")]
288 ParseIntError(#[from] ParseIntError),
289 #[error("Failed to parse color because of incorrect length {0}, expected prefix \"#\" or \"0x\" and length 6")]
290 IncorrectLength(usize),
291 #[error("Failed to parse color becazse of unknown prefix \"{0}\", expected \"#\" or \"0x\"")]
292 UnknownPrefix(String),
293}
294
295#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Eq)]
297#[serde(try_from = "String")]
298#[serde(into = "String")]
299pub struct ThemeColorHex {
300 pub r: u8,
301 pub g: u8,
302 pub b: u8,
303}
304
305impl ThemeColorHex {
306 #[must_use]
308 pub const fn new(r: u8, g: u8, b: u8) -> Self {
309 Self { r, g, b }
310 }
311
312 pub fn from_hex(val: &str) -> Result<Self, ThemeColorHexParseError> {
314 let Some(without_prefix) = val.strip_prefix('#').or(val.strip_prefix("0x")) else {
315 return Err(ThemeColorHexParseError::UnknownPrefix(val.to_string()));
316 };
317
318 if without_prefix.len() != 6 {
320 return Err(ThemeColorHexParseError::IncorrectLength(
321 without_prefix.len(),
322 ));
323 }
324
325 let r = u8::from_str_radix(&without_prefix[0..=1], 16)
326 .map_err(ThemeColorHexParseError::ParseIntError)?;
327 let g = u8::from_str_radix(&without_prefix[2..=3], 16)
328 .map_err(ThemeColorHexParseError::ParseIntError)?;
329 let b = u8::from_str_radix(&without_prefix[4..=5], 16)
330 .map_err(ThemeColorHexParseError::ParseIntError)?;
331
332 Ok(Self { r, g, b })
333 }
334
335 #[inline]
337 #[must_use]
338 pub fn to_hex(&self) -> String {
339 format!("#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
340 }
341}
342
343impl TryFrom<String> for ThemeColorHex {
344 type Error = ThemeColorHexParseError;
345
346 fn try_from(value: String) -> Result<Self, Self::Error> {
347 Self::from_hex(&value)
348 }
349}
350
351impl TryFrom<&str> for ThemeColorHex {
352 type Error = ThemeColorHexParseError;
353
354 fn try_from(value: &str) -> Result<Self, Self::Error> {
355 Self::from_hex(value)
356 }
357}
358
359impl From<ThemeColorHex> for String {
360 fn from(val: ThemeColorHex) -> Self {
361 ThemeColorHex::to_hex(&val)
362 }
363}
364
365impl From<ThemeColorHex> for Color {
366 fn from(val: ThemeColorHex) -> Self {
367 Color::Rgb(val.r, val.g, val.b)
368 }
369}
370
371#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
372#[serde(default)] pub struct ThemeColors {
374 #[serde(skip_serializing_if = "Option::is_none")]
379 pub file_name: Option<String>,
380 pub name: String,
381 pub author: String,
382 pub primary: ThemePrimary,
383 pub cursor: ThemeCursor,
384 pub normal: ThemeNormal,
385 pub bright: ThemeBright,
386}
387
388impl Default for ThemeColors {
389 fn default() -> Self {
390 Self {
391 file_name: None,
392 name: default_name(),
393 author: default_author(),
394 primary: ThemePrimary::default(),
395 cursor: ThemeCursor::default(),
396 normal: ThemeNormal::default(),
397 bright: ThemeBright::default(),
398 }
399 }
400}
401
402impl ThemeColors {
403 #[must_use]
407 pub fn full_default() -> Self {
408 Self {
409 name: "Termusic Default".to_string(),
410 author: "Termusic Developers".to_string(),
411 ..Default::default()
412 }
413 }
414
415 #[must_use]
417 pub fn full_native() -> Self {
418 Self {
419 file_name: None,
420 name: "Native".to_string(),
421 author: "Termusic Developers".to_string(),
422 primary: ThemePrimary::native(),
423 cursor: ThemeCursor::native(),
424 normal: ThemeNormal::native(),
425 bright: ThemeBright::native(),
426 }
427 }
428}
429
430#[derive(Debug, Clone, PartialEq, thiserror::Error)]
432pub enum ThemeColorsParseError {
433 #[error("Failed to parse Theme: {0}")]
434 ThemeColor(#[from] ThemeColorParseError),
435}
436
437impl TryFrom<YAMLTheme> for ThemeColors {
438 type Error = ThemeColorsParseError;
439
440 fn try_from(value: YAMLTheme) -> Result<Self, Self::Error> {
441 let colors = value.colors;
442 Ok(Self {
443 file_name: None,
444 name: colors.name.unwrap_or_else(default_name),
445 author: colors.author.unwrap_or_else(default_author),
446 primary: colors.primary.try_into()?,
447 cursor: colors.cursor.try_into()?,
448 normal: colors.normal.try_into()?,
449 bright: colors.bright.try_into()?,
450 })
451 }
452}
453
454impl ThemeColors {
455 pub fn from_yaml_file(path: &Path) -> anyhow::Result<Self> {
457 let parsed: YAMLTheme = serde_yaml::from_reader(BufReader::new(File::open(path)?))?;
458
459 let mut theme = Self::try_from(parsed)?;
460
461 let file_name = path.file_stem();
462 theme.file_name = file_name.map(|v| v.to_string_lossy().to_string());
463
464 Ok(theme)
465 }
466}
467
468#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
469pub struct ThemePrimary {
470 pub background: ThemeColor,
471 pub foreground: ThemeColor,
472}
473
474impl Default for ThemePrimary {
475 fn default() -> Self {
476 Self {
477 background: ThemeColor::new_hex(0x10, 0x14, 0x21),
478 foreground: ThemeColor::new_hex(0xff, 0xfb, 0xf6),
479 }
480 }
481}
482
483impl ThemePrimary {
484 fn native() -> Self {
485 Self {
486 background: ThemeColor::new_native(),
487 foreground: ThemeColor::new_native(),
488 }
489 }
490}
491
492impl TryFrom<YAMLThemePrimary> for ThemePrimary {
493 type Error = ThemeColorsParseError;
494
495 fn try_from(value: YAMLThemePrimary) -> Result<Self, Self::Error> {
496 Ok(Self {
497 background: value.background.try_into()?,
498 foreground: value.foreground.try_into()?,
499 })
500 }
501}
502
503#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
504#[serde(default)] pub struct ThemeCursor {
506 pub text: ThemeColor,
507 pub cursor: ThemeColor,
508}
509
510impl Default for ThemeCursor {
511 fn default() -> Self {
512 Self {
513 text: ThemeColor::new_hex(0x1e, 0x1e, 0x1e),
514 cursor: default_fff(),
515 }
516 }
517}
518
519impl ThemeCursor {
520 fn native() -> Self {
521 Self {
522 text: ThemeColor::new_native(),
523 cursor: ThemeColor::new_native(),
524 }
525 }
526}
527
528impl TryFrom<YAMLThemeCursor> for ThemeCursor {
529 type Error = ThemeColorsParseError;
530
531 fn try_from(value: YAMLThemeCursor) -> Result<Self, Self::Error> {
532 Ok(Self {
533 text: value.text.try_into()?,
534 cursor: value.cursor.try_into()?,
535 })
536 }
537}
538
539#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
540#[serde(default)] pub struct ThemeNormal {
542 pub black: ThemeColor,
543 pub red: ThemeColor,
544 pub green: ThemeColor,
545 pub yellow: ThemeColor,
546 pub blue: ThemeColor,
547 pub magenta: ThemeColor,
548 pub cyan: ThemeColor,
549 pub white: ThemeColor,
550}
551
552impl Default for ThemeNormal {
553 fn default() -> Self {
554 Self {
555 black: ThemeColor::new_hex(0x2e, 0x2e, 0x2e),
556 red: ThemeColor::new_hex(0xeb, 0x41, 0x29),
557 green: ThemeColor::new_hex(0xab, 0xe0, 0x47),
558 yellow: ThemeColor::new_hex(0xf6, 0xc7, 0x44),
559 blue: ThemeColor::new_hex(0x47, 0xa0, 0xf3),
560 magenta: ThemeColor::new_hex(0x7b, 0x5c, 0xb0),
561 cyan: ThemeColor::new_hex(0x64, 0xdb, 0xed),
562 white: ThemeColor::new_hex(0xe5, 0xe9, 0xf0),
563 }
564 }
565}
566
567impl ThemeNormal {
568 fn native() -> Self {
569 Self {
570 black: ThemeColor::new_native(),
571 red: ThemeColor::new_native(),
572 green: ThemeColor::new_native(),
573 yellow: ThemeColor::new_native(),
574 blue: ThemeColor::new_native(),
575 magenta: ThemeColor::new_native(),
576 cyan: ThemeColor::new_native(),
577 white: ThemeColor::new_native(),
578 }
579 }
580}
581
582impl TryFrom<YAMLThemeNormal> for ThemeNormal {
583 type Error = ThemeColorsParseError;
584
585 fn try_from(value: YAMLThemeNormal) -> Result<Self, Self::Error> {
586 Ok(Self {
587 black: value.black.try_into()?,
588 red: value.red.try_into()?,
589 green: value.green.try_into()?,
590 yellow: value.yellow.try_into()?,
591 blue: value.blue.try_into()?,
592 magenta: value.magenta.try_into()?,
593 cyan: value.cyan.try_into()?,
594 white: value.white.try_into()?,
595 })
596 }
597}
598
599#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
600#[serde(default)] pub struct ThemeBright {
602 pub black: ThemeColor,
603 pub red: ThemeColor,
604 pub green: ThemeColor,
605 pub yellow: ThemeColor,
606 pub blue: ThemeColor,
607 pub magenta: ThemeColor,
608 pub cyan: ThemeColor,
609 pub white: ThemeColor,
610}
611
612impl Default for ThemeBright {
613 fn default() -> Self {
614 Self {
615 black: ThemeColor::new_hex(0x56, 0x56, 0x56),
616 red: ThemeColor::new_hex(0xec, 0x53, 0x57),
617 green: ThemeColor::new_hex(0xc0, 0xe1, 0x7d),
618 yellow: ThemeColor::new_hex(0xf9, 0xda, 0x6a),
619 blue: ThemeColor::new_hex(0x49, 0xa4, 0xf8),
620 magenta: ThemeColor::new_hex(0xa4, 0x7d, 0xe9),
621 cyan: ThemeColor::new_hex(0x99, 0xfa, 0xf2),
622 white: default_fff(),
623 }
624 }
625}
626
627impl ThemeBright {
628 fn native() -> Self {
629 Self {
630 black: ThemeColor::new_native(),
631 red: ThemeColor::new_native(),
632 green: ThemeColor::new_native(),
633 yellow: ThemeColor::new_native(),
634 blue: ThemeColor::new_native(),
635 magenta: ThemeColor::new_native(),
636 cyan: ThemeColor::new_native(),
637 white: ThemeColor::new_native(),
638 }
639 }
640}
641
642impl TryFrom<YAMLThemeBright> for ThemeBright {
643 type Error = ThemeColorsParseError;
644
645 fn try_from(value: YAMLThemeBright) -> Result<Self, Self::Error> {
646 Ok(Self {
647 black: value.black.try_into()?,
648 red: value.red.try_into()?,
649 green: value.green.try_into()?,
650 yellow: value.yellow.try_into()?,
651 blue: value.blue.try_into()?,
652 magenta: value.magenta.try_into()?,
653 cyan: value.cyan.try_into()?,
654 white: value.white.try_into()?,
655 })
656 }
657}
658
659#[inline]
660fn default_name() -> String {
661 "empty name".to_string()
662}
663
664#[inline]
665fn default_author() -> String {
666 "empty author".to_string()
667}
668
669#[inline]
670fn default_fff() -> ThemeColor {
671 ThemeColor::new_hex(0xFF, 0xFF, 0xFF)
672}
673
674mod v1_interop {
675 use super::{
676 ThemeBright, ThemeColor, ThemeColorHex, ThemeColors, ThemeCursor, ThemeNormal,
677 ThemePrimary, ThemeWrap,
678 };
679 use crate::config::v1;
680
681 impl From<v1::AlacrittyColor> for ThemeColorHex {
682 fn from(value: v1::AlacrittyColor) -> Self {
683 Self {
684 r: value.r,
685 g: value.g,
686 b: value.b,
687 }
688 }
689 }
690
691 impl From<v1::AlacrittyColor> for ThemeColor {
692 fn from(value: v1::AlacrittyColor) -> Self {
693 Self::Hex(value.into())
694 }
695 }
696
697 impl From<&v1::Alacritty> for ThemeColors {
698 fn from(value: &v1::Alacritty) -> Self {
699 Self {
700 file_name: None,
701 name: value.name.clone(),
702 author: value.author.clone(),
703 primary: ThemePrimary {
704 background: value.background.into(),
705 foreground: value.foreground.into(),
706 },
707 cursor: ThemeCursor {
708 text: value.text.into(),
709 cursor: value.cursor.into(),
710 },
711 normal: ThemeNormal {
712 black: value.black.into(),
713 red: value.red.into(),
714 green: value.green.into(),
715 yellow: value.yellow.into(),
716 blue: value.blue.into(),
717 magenta: value.magenta.into(),
718 cyan: value.cyan.into(),
719 white: value.white.into(),
720 },
721 bright: ThemeBright {
722 black: value.light_black.into(),
723 red: value.light_red.into(),
724 green: value.light_green.into(),
725 yellow: value.light_yellow.into(),
726 blue: value.light_blue.into(),
727 magenta: value.light_magenta.into(),
728 cyan: value.light_cyan.into(),
729 white: value.light_white.into(),
730 },
731 }
732 }
733 }
734
735 impl From<&v1::Settings> for ThemeWrap {
736 fn from(value: &v1::Settings) -> Self {
737 Self {
738 theme: (&value.style_color_symbol.alacritty_theme).into(),
739 style: value.into(),
740 }
741 }
742 }
743
744 #[cfg(test)]
745 mod tests {
746 use super::*;
747
748 #[test]
749 fn should_convert_default_without_error() {
750 let converted: ThemeColors = (&v1::StyleColorSymbol::default().alacritty_theme).into();
751
752 assert_eq!(
753 converted,
754 ThemeColors {
755 file_name: None,
756 name: "default".into(),
757 author: "Larry Hao".into(),
758 primary: ThemePrimary {
759 background: "#101421".try_into().unwrap(),
760 foreground: "#fffbf6".try_into().unwrap()
761 },
762 cursor: ThemeCursor {
763 text: "#1E1E1E".try_into().unwrap(),
764 cursor: "#FFFFFF".try_into().unwrap()
765 },
766 normal: ThemeNormal {
767 black: "#2e2e2e".try_into().unwrap(),
768 red: "#eb4129".try_into().unwrap(),
769 green: "#abe047".try_into().unwrap(),
770 yellow: "#f6c744".try_into().unwrap(),
771 blue: "#47a0f3".try_into().unwrap(),
772 magenta: "#7b5cb0".try_into().unwrap(),
773 cyan: "#64dbed".try_into().unwrap(),
774 white: "#e5e9f0".try_into().unwrap()
775 },
776 bright: ThemeBright {
777 black: "#565656".try_into().unwrap(),
778 red: "#ec5357".try_into().unwrap(),
779 green: "#c0e17d".try_into().unwrap(),
780 yellow: "#f9da6a".try_into().unwrap(),
781 blue: "#49a4f8".try_into().unwrap(),
782 magenta: "#a47de9".try_into().unwrap(),
783 cyan: "#99faf2".try_into().unwrap(),
784 white: "#ffffff".try_into().unwrap()
785 }
786 }
787 );
788 }
789 }
790}
791
792#[cfg(test)]
793mod tests {
794 use super::ThemeColors;
795
796 mod theme_color {
797 use super::super::ThemeColor;
798
799 #[test]
800 fn should_parse_hex() {
801 assert_eq!(
802 ThemeColor::new_hex(1, 2, 3),
803 ThemeColor::try_from("#010203").unwrap()
804 );
805 assert_eq!(
806 ThemeColor::new_hex(1, 2, 3),
807 ThemeColor::try_from("0x010203").unwrap()
808 );
809 }
810
811 #[test]
812 fn should_parse_native() {
813 assert_eq!(
814 ThemeColor::new_native(),
815 ThemeColor::try_from("native").unwrap()
816 );
817 }
818
819 #[test]
820 fn should_serialize() {
821 assert_eq!(ThemeColor::new_hex(1, 2, 3).to_string(), "#010203");
822 assert_eq!(ThemeColor::new_native().to_string(), "native");
823 }
824 }
825
826 mod theme_color_hex {
827 use super::super::ThemeColorHex;
828
829 #[test]
830 fn should_parse_hashtag() {
831 assert_eq!(
832 ThemeColorHex::new(1, 2, 3),
833 ThemeColorHex::from_hex("#010203").unwrap()
834 );
835 assert_eq!(
836 ThemeColorHex::new(255, 255, 255),
837 ThemeColorHex::from_hex("#ffffff").unwrap()
838 );
839 assert_eq!(
840 ThemeColorHex::new(0, 0, 0),
841 ThemeColorHex::from_hex("#000000").unwrap()
842 );
843 }
844
845 #[test]
846 fn should_parse_0x() {
847 assert_eq!(
848 ThemeColorHex::new(1, 2, 3),
849 ThemeColorHex::from_hex("0x010203").unwrap()
850 );
851 assert_eq!(
852 ThemeColorHex::new(255, 255, 255),
853 ThemeColorHex::from_hex("0xffffff").unwrap()
854 );
855 assert_eq!(
856 ThemeColorHex::new(0, 0, 0),
857 ThemeColorHex::from_hex("0x000000").unwrap()
858 );
859 }
860 }
861
862 #[test]
863 fn should_default() {
864 let _ = ThemeColors::default();
866 }
867}