1use core::fmt;
72
73use bitflags::bitflags;
74pub use color::{Color, ParseColorError};
75use stylize::ColorDebugKind;
76pub use stylize::{Styled, Stylize};
77
78#[cfg(feature = "anstyle")]
79mod anstyle;
80mod color;
81pub mod palette;
82#[cfg(feature = "palette")]
83mod palette_conversion;
84#[macro_use]
85mod stylize;
86
87bitflags! {
88 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
103 #[derive(Default, Clone, Copy, Eq, PartialEq, Hash)]
104 pub struct Modifier: u16 {
105 const BOLD = 0b0000_0000_0001;
106 const DIM = 0b0000_0000_0010;
107 const ITALIC = 0b0000_0000_0100;
108 const UNDERLINED = 0b0000_0000_1000;
109 const SLOW_BLINK = 0b0000_0001_0000;
110 const RAPID_BLINK = 0b0000_0010_0000;
111 const REVERSED = 0b0000_0100_0000;
112 const HIDDEN = 0b0000_1000_0000;
113 const CROSSED_OUT = 0b0001_0000_0000;
114 }
115}
116
117impl fmt::Debug for Modifier {
121 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124 if self.is_empty() {
125 return write!(f, "NONE");
126 }
127 write!(f, "{}", self.0)
128 }
129}
130
131#[derive(Default, Clone, Copy, Eq, PartialEq, Hash)]
238#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
239pub struct Style {
240 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
242 pub fg: Option<Color>,
243 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
245 pub bg: Option<Color>,
246 #[cfg(feature = "underline-color")]
248 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
249 pub underline_color: Option<Color>,
250 #[cfg_attr(
252 feature = "serde",
253 serde(
254 default,
255 skip_serializing_if = "Modifier::is_empty",
256 deserialize_with = "deserialize_modifier"
257 )
258 )]
259 pub add_modifier: Modifier,
260 #[cfg_attr(
262 feature = "serde",
263 serde(
264 default,
265 skip_serializing_if = "Modifier::is_empty",
266 deserialize_with = "deserialize_modifier"
267 )
268 )]
269 pub sub_modifier: Modifier,
270}
271
272#[cfg(feature = "serde")]
273fn deserialize_modifier<'de, D>(deserializer: D) -> Result<Modifier, D::Error>
279where
280 D: serde::Deserializer<'de>,
281{
282 use serde::Deserialize;
283
284 Option::<Modifier>::deserialize(deserializer)
285 .map(|modifier| modifier.unwrap_or_else(Modifier::empty))
286}
287
288impl fmt::Debug for Style {
291 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
292 f.write_str("Style::new()")?;
293 self.fmt_stylize(f)?;
294 Ok(())
295 }
296}
297
298impl Style {
299 pub const fn new() -> Self {
301 Self {
302 fg: None,
303 bg: None,
304 #[cfg(feature = "underline-color")]
305 underline_color: None,
306 add_modifier: Modifier::empty(),
307 sub_modifier: Modifier::empty(),
308 }
309 }
310
311 pub const fn reset() -> Self {
313 Self {
314 fg: Some(Color::Reset),
315 bg: Some(Color::Reset),
316 #[cfg(feature = "underline-color")]
317 underline_color: Some(Color::Reset),
318 add_modifier: Modifier::empty(),
319 sub_modifier: Modifier::all(),
320 }
321 }
322
323 #[must_use = "`fg` returns the modified style without modifying the original"]
335 pub const fn fg(mut self, color: Color) -> Self {
336 self.fg = Some(color);
337 self
338 }
339
340 #[must_use = "`bg` returns the modified style without modifying the original"]
352 pub const fn bg(mut self, color: Color) -> Self {
353 self.bg = Some(color);
354 self
355 }
356
357 #[cfg(feature = "underline-color")]
386 #[must_use = "`underline_color` returns the modified style without modifying the original"]
387 pub const fn underline_color(mut self, color: Color) -> Self {
388 self.underline_color = Some(color);
389 self
390 }
391
392 #[must_use = "`add_modifier` returns the modified style without modifying the original"]
408 pub const fn add_modifier(mut self, modifier: Modifier) -> Self {
409 self.sub_modifier = self.sub_modifier.difference(modifier);
410 self.add_modifier = self.add_modifier.union(modifier);
411 self
412 }
413
414 #[must_use = "`remove_modifier` returns the modified style without modifying the original"]
430 pub const fn remove_modifier(mut self, modifier: Modifier) -> Self {
431 self.add_modifier = self.add_modifier.difference(modifier);
432 self.sub_modifier = self.sub_modifier.union(modifier);
433 self
434 }
435
436 pub const fn has_modifier(self, modifier: Modifier) -> bool {
449 self.add_modifier.contains(modifier) && !self.sub_modifier.contains(modifier)
450 }
451
452 #[must_use = "`patch` returns the modified style without modifying the original"]
471 pub fn patch<S: Into<Self>>(mut self, other: S) -> Self {
472 let other = other.into();
473 self.fg = other.fg.or(self.fg);
474 self.bg = other.bg.or(self.bg);
475
476 #[cfg(feature = "underline-color")]
477 {
478 self.underline_color = other.underline_color.or(self.underline_color);
479 }
480
481 self.add_modifier.remove(other.sub_modifier);
482 self.add_modifier.insert(other.add_modifier);
483 self.sub_modifier.remove(other.add_modifier);
484 self.sub_modifier.insert(other.sub_modifier);
485
486 self
487 }
488
489 pub(crate) fn fmt_stylize(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 use fmt::Debug;
494 if let Some(fg) = self.fg {
495 fg.stylize_debug(ColorDebugKind::Foreground).fmt(f)?;
496 }
497 if let Some(bg) = self.bg {
498 bg.stylize_debug(ColorDebugKind::Background).fmt(f)?;
499 }
500 #[cfg(feature = "underline-color")]
501 if let Some(underline_color) = self.underline_color {
502 underline_color
503 .stylize_debug(ColorDebugKind::Underline)
504 .fmt(f)?;
505 }
506 for modifier in self.add_modifier.iter() {
507 match modifier {
508 Modifier::BOLD => f.write_str(".bold()")?,
509 Modifier::DIM => f.write_str(".dim()")?,
510 Modifier::ITALIC => f.write_str(".italic()")?,
511 Modifier::UNDERLINED => f.write_str(".underlined()")?,
512 Modifier::SLOW_BLINK => f.write_str(".slow_blink()")?,
513 Modifier::RAPID_BLINK => f.write_str(".rapid_blink()")?,
514 Modifier::REVERSED => f.write_str(".reversed()")?,
515 Modifier::HIDDEN => f.write_str(".hidden()")?,
516 Modifier::CROSSED_OUT => f.write_str(".crossed_out()")?,
517 _ => f.write_fmt(format_args!(".add_modifier(Modifier::{modifier:?})"))?,
518 }
519 }
520 for modifier in self.sub_modifier.iter() {
521 match modifier {
522 Modifier::BOLD => f.write_str(".not_bold()")?,
523 Modifier::DIM => f.write_str(".not_dim()")?,
524 Modifier::ITALIC => f.write_str(".not_italic()")?,
525 Modifier::UNDERLINED => f.write_str(".not_underlined()")?,
526 Modifier::SLOW_BLINK => f.write_str(".not_slow_blink()")?,
527 Modifier::RAPID_BLINK => f.write_str(".not_rapid_blink()")?,
528 Modifier::REVERSED => f.write_str(".not_reversed()")?,
529 Modifier::HIDDEN => f.write_str(".not_hidden()")?,
530 Modifier::CROSSED_OUT => f.write_str(".not_crossed_out()")?,
531 _ => f.write_fmt(format_args!(".remove_modifier(Modifier::{modifier:?})"))?,
532 }
533 }
534 Ok(())
535 }
536
537 color!(pub const Color::Black, black(), on_black() -> Self);
538 color!(pub const Color::Red, red(), on_red() -> Self);
539 color!(pub const Color::Green, green(), on_green() -> Self);
540 color!(pub const Color::Yellow, yellow(), on_yellow() -> Self);
541 color!(pub const Color::Blue, blue(), on_blue() -> Self);
542 color!(pub const Color::Magenta, magenta(), on_magenta() -> Self);
543 color!(pub const Color::Cyan, cyan(), on_cyan() -> Self);
544 color!(pub const Color::Gray, gray(), on_gray() -> Self);
545 color!(pub const Color::DarkGray, dark_gray(), on_dark_gray() -> Self);
546 color!(pub const Color::LightRed, light_red(), on_light_red() -> Self);
547 color!(pub const Color::LightGreen, light_green(), on_light_green() -> Self);
548 color!(pub const Color::LightYellow, light_yellow(), on_light_yellow() -> Self);
549 color!(pub const Color::LightBlue, light_blue(), on_light_blue() -> Self);
550 color!(pub const Color::LightMagenta, light_magenta(), on_light_magenta() -> Self);
551 color!(pub const Color::LightCyan, light_cyan(), on_light_cyan() -> Self);
552 color!(pub const Color::White, white(), on_white() -> Self);
553
554 modifier!(pub const Modifier::BOLD, bold(), not_bold() -> Self);
555 modifier!(pub const Modifier::DIM, dim(), not_dim() -> Self);
556 modifier!(pub const Modifier::ITALIC, italic(), not_italic() -> Self);
557 modifier!(pub const Modifier::UNDERLINED, underlined(), not_underlined() -> Self);
558 modifier!(pub const Modifier::SLOW_BLINK, slow_blink(), not_slow_blink() -> Self);
559 modifier!(pub const Modifier::RAPID_BLINK, rapid_blink(), not_rapid_blink() -> Self);
560 modifier!(pub const Modifier::REVERSED, reversed(), not_reversed() -> Self);
561 modifier!(pub const Modifier::HIDDEN, hidden(), not_hidden() -> Self);
562 modifier!(pub const Modifier::CROSSED_OUT, crossed_out(), not_crossed_out() -> Self);
563}
564
565impl From<Color> for Style {
566 fn from(color: Color) -> Self {
578 Self::new().fg(color)
579 }
580}
581
582impl From<(Color, Color)> for Style {
583 fn from((fg, bg): (Color, Color)) -> Self {
596 Self::new().fg(fg).bg(bg)
597 }
598}
599
600impl From<Modifier> for Style {
601 fn from(modifier: Modifier) -> Self {
616 Self::new().add_modifier(modifier)
617 }
618}
619
620impl From<(Modifier, Modifier)> for Style {
621 fn from((add_modifier, sub_modifier): (Modifier, Modifier)) -> Self {
632 Self::new()
633 .add_modifier(add_modifier)
634 .remove_modifier(sub_modifier)
635 }
636}
637
638impl From<(Color, Modifier)> for Style {
639 fn from((fg, modifier): (Color, Modifier)) -> Self {
652 Self::new().fg(fg).add_modifier(modifier)
653 }
654}
655
656impl From<(Color, Color, Modifier)> for Style {
657 fn from((fg, bg, modifier): (Color, Color, Modifier)) -> Self {
670 Self::new().fg(fg).bg(bg).add_modifier(modifier)
671 }
672}
673
674impl From<(Color, Color, Modifier, Modifier)> for Style {
675 fn from((fg, bg, add_modifier, sub_modifier): (Color, Color, Modifier, Modifier)) -> Self {
692 Self::new()
693 .fg(fg)
694 .bg(bg)
695 .add_modifier(add_modifier)
696 .remove_modifier(sub_modifier)
697 }
698}
699
700#[cfg(test)]
701mod tests {
702 use alloc::format;
703
704 use rstest::rstest;
705
706 use super::*;
707
708 #[rstest]
709 #[case(Style::new(), "Style::new()")]
710 #[case(Style::default(), "Style::new()")]
711 #[case(Style::new().red(), "Style::new().red()")]
712 #[case(Style::new().on_blue(), "Style::new().on_blue()")]
713 #[case(Style::new().bold(), "Style::new().bold()")]
714 #[case(Style::new().not_italic(), "Style::new().not_italic()")]
715 #[case(
716 Style::new().red().on_blue().bold().italic().not_dim().not_hidden(),
717 "Style::new().red().on_blue().bold().italic().not_dim().not_hidden()"
718 )]
719 fn debug(#[case] style: Style, #[case] expected: &'static str) {
720 assert_eq!(format!("{style:?}"), expected);
721 }
722
723 #[test]
724 fn combined_patch_gives_same_result_as_individual_patch() {
725 let styles = [
726 Style::new(),
727 Style::new().fg(Color::Yellow),
728 Style::new().bg(Color::Yellow),
729 Style::new().add_modifier(Modifier::BOLD),
730 Style::new().remove_modifier(Modifier::BOLD),
731 Style::new().add_modifier(Modifier::ITALIC),
732 Style::new().remove_modifier(Modifier::ITALIC),
733 Style::new().add_modifier(Modifier::ITALIC | Modifier::BOLD),
734 Style::new().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
735 ];
736 for &a in &styles {
737 for &b in &styles {
738 for &c in &styles {
739 for &d in &styles {
740 assert_eq!(
741 Style::new().patch(a).patch(b).patch(c).patch(d),
742 Style::new().patch(a.patch(b.patch(c.patch(d))))
743 );
744 }
745 }
746 }
747 }
748 }
749
750 #[test]
751 fn combine_individual_modifiers() {
752 use crate::buffer::Buffer;
753 use crate::layout::Rect;
754
755 let mods = [
756 Modifier::BOLD,
757 Modifier::DIM,
758 Modifier::ITALIC,
759 Modifier::UNDERLINED,
760 Modifier::SLOW_BLINK,
761 Modifier::RAPID_BLINK,
762 Modifier::REVERSED,
763 Modifier::HIDDEN,
764 Modifier::CROSSED_OUT,
765 ];
766
767 let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
768
769 for m in mods {
770 buffer[(0, 0)].set_style(Style::reset());
771 buffer[(0, 0)].set_style(Style::new().add_modifier(m));
772 let style = buffer[(0, 0)].style();
773 assert!(style.add_modifier.contains(m));
774 assert!(!style.sub_modifier.contains(m));
775 }
776 }
777
778 #[rstest]
779 #[case(Modifier::empty(), "NONE")]
780 #[case(Modifier::BOLD, "BOLD")]
781 #[case(Modifier::DIM, "DIM")]
782 #[case(Modifier::ITALIC, "ITALIC")]
783 #[case(Modifier::UNDERLINED, "UNDERLINED")]
784 #[case(Modifier::SLOW_BLINK, "SLOW_BLINK")]
785 #[case(Modifier::RAPID_BLINK, "RAPID_BLINK")]
786 #[case(Modifier::REVERSED, "REVERSED")]
787 #[case(Modifier::HIDDEN, "HIDDEN")]
788 #[case(Modifier::CROSSED_OUT, "CROSSED_OUT")]
789 #[case(Modifier::BOLD | Modifier::DIM, "BOLD | DIM")]
790 #[case(
791 Modifier::all(),
792 "BOLD | DIM | ITALIC | UNDERLINED | SLOW_BLINK | RAPID_BLINK | REVERSED | HIDDEN | CROSSED_OUT"
793 )]
794 fn modifier_debug(#[case] modifier: Modifier, #[case] expected: &str) {
795 assert_eq!(format!("{modifier:?}"), expected);
796 }
797
798 #[test]
799 fn style_can_be_const() {
800 const RED: Color = Color::Red;
801 const BLACK: Color = Color::Black;
802 const BOLD: Modifier = Modifier::BOLD;
803 const ITALIC: Modifier = Modifier::ITALIC;
804
805 const _RESET: Style = Style::reset();
806 const _RED_FG: Style = Style::new().fg(RED);
807 const _RED_FG_SHORT: Style = Style::new().red();
808 const _BLACK_BG: Style = Style::new().bg(BLACK);
809 const _BLACK_BG_SHORT: Style = Style::new().on_black();
810 const _ADD_BOLD: Style = Style::new().add_modifier(BOLD);
811 const _ADD_BOLD_SHORT: Style = Style::new().bold();
812 const _REMOVE_ITALIC: Style = Style::new().remove_modifier(ITALIC);
813 const _REMOVE_ITALIC_SHORT: Style = Style::new().not_italic();
814 const ALL: Style = Style::new()
815 .fg(RED)
816 .bg(BLACK)
817 .add_modifier(BOLD)
818 .remove_modifier(ITALIC);
819 const ALL_SHORT: Style = Style::new().red().on_black().bold().not_italic();
820 assert_eq!(
821 ALL,
822 Style::new()
823 .fg(Color::Red)
824 .bg(Color::Black)
825 .add_modifier(Modifier::BOLD)
826 .remove_modifier(Modifier::ITALIC)
827 );
828 assert_eq!(ALL, ALL_SHORT);
829 }
830
831 #[test]
832 fn has_modifier_checks() {
833 let style = Style::new().add_modifier(Modifier::BOLD | Modifier::ITALIC);
835 assert!(style.has_modifier(Modifier::BOLD));
836 assert!(style.has_modifier(Modifier::ITALIC));
837 assert!(!style.has_modifier(Modifier::UNDERLINED));
838
839 let style = Style::new()
841 .add_modifier(Modifier::BOLD | Modifier::ITALIC)
842 .remove_modifier(Modifier::ITALIC);
843 assert!(style.has_modifier(Modifier::BOLD));
844 assert!(!style.has_modifier(Modifier::ITALIC));
845
846 let style = Style::new().add_modifier(Modifier::BOLD | Modifier::ITALIC);
848 let patched = style.patch(Style::new().remove_modifier(Modifier::ITALIC));
849 assert!(patched.has_modifier(Modifier::BOLD));
850 assert!(!patched.has_modifier(Modifier::ITALIC));
851 }
852
853 #[rstest]
854 #[case(Style::new().black(), Color::Black)]
855 #[case(Style::new().red(), Color::Red)]
856 #[case(Style::new().green(), Color::Green)]
857 #[case(Style::new().yellow(), Color::Yellow)]
858 #[case(Style::new().blue(), Color::Blue)]
859 #[case(Style::new().magenta(), Color::Magenta)]
860 #[case(Style::new().cyan(), Color::Cyan)]
861 #[case(Style::new().white(), Color::White)]
862 #[case(Style::new().gray(), Color::Gray)]
863 #[case(Style::new().dark_gray(), Color::DarkGray)]
864 #[case(Style::new().light_red(), Color::LightRed)]
865 #[case(Style::new().light_green(), Color::LightGreen)]
866 #[case(Style::new().light_yellow(), Color::LightYellow)]
867 #[case(Style::new().light_blue(), Color::LightBlue)]
868 #[case(Style::new().light_magenta(), Color::LightMagenta)]
869 #[case(Style::new().light_cyan(), Color::LightCyan)]
870 #[case(Style::new().white(), Color::White)]
871 fn fg_can_be_stylized(#[case] stylized: Style, #[case] expected: Color) {
872 assert_eq!(stylized, Style::new().fg(expected));
873 }
874
875 #[rstest]
876 #[case(Style::new().on_black(), Color::Black)]
877 #[case(Style::new().on_red(), Color::Red)]
878 #[case(Style::new().on_green(), Color::Green)]
879 #[case(Style::new().on_yellow(), Color::Yellow)]
880 #[case(Style::new().on_blue(), Color::Blue)]
881 #[case(Style::new().on_magenta(), Color::Magenta)]
882 #[case(Style::new().on_cyan(), Color::Cyan)]
883 #[case(Style::new().on_white(), Color::White)]
884 #[case(Style::new().on_gray(), Color::Gray)]
885 #[case(Style::new().on_dark_gray(), Color::DarkGray)]
886 #[case(Style::new().on_light_red(), Color::LightRed)]
887 #[case(Style::new().on_light_green(), Color::LightGreen)]
888 #[case(Style::new().on_light_yellow(), Color::LightYellow)]
889 #[case(Style::new().on_light_blue(), Color::LightBlue)]
890 #[case(Style::new().on_light_magenta(), Color::LightMagenta)]
891 #[case(Style::new().on_light_cyan(), Color::LightCyan)]
892 #[case(Style::new().on_white(), Color::White)]
893 fn bg_can_be_stylized(#[case] stylized: Style, #[case] expected: Color) {
894 assert_eq!(stylized, Style::new().bg(expected));
895 }
896
897 #[rstest]
898 #[case(Style::new().bold(), Modifier::BOLD)]
899 #[case(Style::new().dim(), Modifier::DIM)]
900 #[case(Style::new().italic(), Modifier::ITALIC)]
901 #[case(Style::new().underlined(), Modifier::UNDERLINED)]
902 #[case(Style::new().slow_blink(), Modifier::SLOW_BLINK)]
903 #[case(Style::new().rapid_blink(), Modifier::RAPID_BLINK)]
904 #[case(Style::new().reversed(), Modifier::REVERSED)]
905 #[case(Style::new().hidden(), Modifier::HIDDEN)]
906 #[case(Style::new().crossed_out(), Modifier::CROSSED_OUT)]
907 fn add_modifier_can_be_stylized(#[case] stylized: Style, #[case] expected: Modifier) {
908 assert_eq!(stylized, Style::new().add_modifier(expected));
909 }
910
911 #[rstest]
912 #[case(Style::new().not_bold(), Modifier::BOLD)]
913 #[case(Style::new().not_dim(), Modifier::DIM)]
914 #[case(Style::new().not_italic(), Modifier::ITALIC)]
915 #[case(Style::new().not_underlined(), Modifier::UNDERLINED)]
916 #[case(Style::new().not_slow_blink(), Modifier::SLOW_BLINK)]
917 #[case(Style::new().not_rapid_blink(), Modifier::RAPID_BLINK)]
918 #[case(Style::new().not_reversed(), Modifier::REVERSED)]
919 #[case(Style::new().not_hidden(), Modifier::HIDDEN)]
920 #[case(Style::new().not_crossed_out(), Modifier::CROSSED_OUT)]
921 fn remove_modifier_can_be_stylized(#[case] stylized: Style, #[case] expected: Modifier) {
922 assert_eq!(stylized, Style::new().remove_modifier(expected));
923 }
924
925 #[test]
926 fn from_color() {
927 assert_eq!(Style::from(Color::Red), Style::new().fg(Color::Red));
928 }
929
930 #[test]
931 fn from_color_color() {
932 assert_eq!(
933 Style::from((Color::Red, Color::Blue)),
934 Style::new().fg(Color::Red).bg(Color::Blue)
935 );
936 }
937
938 #[test]
939 fn from_modifier() {
940 assert_eq!(
941 Style::from(Modifier::BOLD | Modifier::ITALIC),
942 Style::new()
943 .add_modifier(Modifier::BOLD)
944 .add_modifier(Modifier::ITALIC)
945 );
946 }
947
948 #[test]
949 fn from_modifier_modifier() {
950 assert_eq!(
951 Style::from((Modifier::BOLD | Modifier::ITALIC, Modifier::DIM)),
952 Style::new()
953 .add_modifier(Modifier::BOLD)
954 .add_modifier(Modifier::ITALIC)
955 .remove_modifier(Modifier::DIM)
956 );
957 }
958
959 #[test]
960 fn from_color_modifier() {
961 assert_eq!(
962 Style::from((Color::Red, Modifier::BOLD | Modifier::ITALIC)),
963 Style::new()
964 .fg(Color::Red)
965 .add_modifier(Modifier::BOLD)
966 .add_modifier(Modifier::ITALIC)
967 );
968 }
969
970 #[test]
971 fn from_color_color_modifier() {
972 assert_eq!(
973 Style::from((Color::Red, Color::Blue, Modifier::BOLD | Modifier::ITALIC)),
974 Style::new()
975 .fg(Color::Red)
976 .bg(Color::Blue)
977 .add_modifier(Modifier::BOLD)
978 .add_modifier(Modifier::ITALIC)
979 );
980 }
981
982 #[test]
983 fn from_color_color_modifier_modifier() {
984 assert_eq!(
985 Style::from((
986 Color::Red,
987 Color::Blue,
988 Modifier::BOLD | Modifier::ITALIC,
989 Modifier::DIM
990 )),
991 Style::new()
992 .fg(Color::Red)
993 .bg(Color::Blue)
994 .add_modifier(Modifier::BOLD)
995 .add_modifier(Modifier::ITALIC)
996 .remove_modifier(Modifier::DIM)
997 );
998 }
999
1000 #[cfg(feature = "serde")]
1001 #[test]
1002 fn serialize_then_deserialize() {
1003 let style = Style {
1004 fg: Some(Color::Rgb(255, 0, 255)),
1005 bg: Some(Color::White),
1006 #[cfg(feature = "underline-color")]
1007 underline_color: Some(Color::Indexed(3)),
1008 add_modifier: Modifier::UNDERLINED,
1009 sub_modifier: Modifier::CROSSED_OUT,
1010 };
1011
1012 let json_str = serde_json::to_string(&style).unwrap();
1013 let json_value: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1014
1015 let mut expected_json = serde_json::json!({
1016 "fg": "#FF00FF",
1017 "bg": "White",
1018 "add_modifier": "UNDERLINED",
1019 "sub_modifier": "CROSSED_OUT"
1020 });
1021
1022 #[cfg(feature = "underline-color")]
1023 {
1024 expected_json
1025 .as_object_mut()
1026 .unwrap()
1027 .insert("underline_color".into(), "3".into());
1028 }
1029
1030 assert_eq!(json_value, expected_json);
1031
1032 let deserialized: Style = serde_json::from_str(&json_str).unwrap();
1033 assert_eq!(deserialized, style);
1034 }
1035
1036 #[cfg(feature = "serde")]
1037 #[test]
1038 fn deserialize_defaults() {
1039 let style = Style {
1040 fg: None,
1041 bg: None,
1042 #[cfg(feature = "underline-color")]
1043 underline_color: None,
1044 add_modifier: Modifier::empty(),
1045 sub_modifier: Modifier::empty(),
1046 };
1047
1048 let json_str = serde_json::to_string(&style).unwrap();
1049 assert_eq!(json_str, "{}");
1050
1051 let deserialized: Style = serde_json::from_str(&json_str).unwrap();
1052 assert_eq!(deserialized, style);
1053 }
1054
1055 #[cfg(feature = "serde")]
1056 #[test]
1057 fn deserialize_null_modifiers() {
1058 let json_value = serde_json::json!({
1059 "add_modifier": serde_json::Value::Null,
1060 "sub_modifier": serde_json::Value::Null
1061 });
1062 let json_str = serde_json::to_string(&json_value).unwrap();
1063
1064 let style: Style = serde_json::from_str(&json_str).unwrap();
1065
1066 assert!(style.add_modifier.is_empty());
1067 assert!(style.sub_modifier.is_empty());
1068 }
1069}