vortaroboto

Log | Files | Refs | README

colors.rs (4520B)


      1 use irc::proto::Message;
      2 use std::fmt::Display;
      3 use std::str::FromStr;
      4 
      5 #[derive(Clone,Copy)]
      6 pub enum ColorKind {
      7     White,
      8     Black,
      9     Blue,
     10     Green,
     11     Red,
     12     Brown,
     13     Magenta,
     14     Orange,
     15     Yellow,
     16     LightGreen,
     17     Cyan,
     18     LightCyan,
     19     LightBlue,
     20     Pink,
     21     Grey,
     22     LightGrey,
     23 }
     24 
     25 impl ColorKind {
     26     fn as_str(&self) -> &'static str {
     27         match self {
     28             Self::White => "00",
     29             Self::Black => "01",
     30             Self::Blue => "02",
     31             Self::Green => "03",
     32             Self::Red => "04",
     33             Self::Brown => "05",
     34             Self::Magenta => "06",
     35             Self::Orange => "07",
     36             Self::Yellow => "08",
     37             Self::LightGreen => "09",
     38             Self::Cyan => "10",
     39             Self::LightCyan => "11",
     40             Self::LightBlue => "12",
     41             Self::Pink => "13",
     42             Self::Grey => "14",
     43             Self::LightGrey => "15",
     44         }
     45     }
     46 }
     47 
     48 impl Display for ColorKind {
     49     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     50         write!(f, "{}", self.as_str())
     51     }
     52 }
     53 
     54 pub enum FormattingKind {
     55     Bold,
     56     Italics,
     57     Underline,
     58     Strikethrough,
     59     Monospace,
     60     Color {
     61         fg: ColorKind,
     62         bg: Option<ColorKind>,
     63     },
     64     ReverseColor,
     65     None,
     66 }
     67 
     68 impl FormattingKind {
     69     pub fn is_none(&self) -> bool {
     70         match self {
     71             Self::None => true,
     72             _ => false
     73         }
     74     }
     75 }
     76 
     77 impl Display for FormattingKind {
     78     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     79         if let Self::Color { fg, bg } = self {
     80             write!(f, "\u{0003}{}", fg)?;
     81             if let Some(b) = bg {
     82                 write!(f, ",{}", b)
     83             } else {
     84                 Ok(())
     85             }
     86         } else {
     87             write!(
     88                 f,
     89                 "{}",
     90                 match self {
     91                     Self::Bold => "\u{0002}",
     92                     Self::Italics => "\u{001D}",
     93                     Self::Underline => "\u{001F}",
     94                     Self::Strikethrough => "\u{001E}",
     95                     Self::Monospace => "\u{0011}",
     96                     Self::ReverseColor => "\u{0016}",
     97                     Self::None => "\u{000F}",
     98                     Self::Color { .. } => unreachable!(),
     99                 }
    100             )
    101         }
    102     }
    103 }
    104 
    105 pub struct Formatted<T: Display> {
    106     kinds: Vec<FormattingKind>,
    107     content: T,
    108 }
    109 
    110 impl<T: Display> Display for Formatted<T> {
    111     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    112         if self.kinds.len() == 1 && self.kinds.get(0).unwrap().is_none() {
    113             return self.content.fmt(f);
    114         }
    115 
    116         for (i, k) in self.kinds.iter().enumerate() {
    117             write!(f, "{}", k)?;
    118             if let FormattingKind::Color { bg: None, .. } = k {
    119                 // Avoid eating , here
    120                 if self.content.to_string().starts_with(',') && i == self.kinds.len() - 1 {
    121                     write!(
    122                         f,
    123                         "{}{}",
    124                         FormattingKind::ReverseColor,
    125                         FormattingKind::ReverseColor
    126                     )?;
    127                 }
    128             }
    129         }
    130         write!(f, "{}{}", self.content, FormattingKind::None)
    131     }
    132 }
    133 
    134 impl<T: Display> Formatted<T> {
    135     pub fn new(kinds: Vec<FormattingKind>, content: T) -> Self {
    136         Formatted { kinds, content }
    137     }
    138 
    139     pub fn simple(kind: FormattingKind, content: T) -> Self {
    140         Formatted { kinds: vec![kind], content}
    141     }
    142 }
    143 
    144 impl<'a> Formatted<&'a str> {
    145     pub fn from_str<'b: 'a>(s: &'b str) -> Self {
    146         Self::new(vec![FormattingKind::None], s)
    147     }
    148 }
    149 
    150 macro_rules! irc_fmt {
    151     ($fmt:expr => $inner:expr) => {
    152         Formatted::simple($fmt, $inner)
    153     };
    154     ($($fmts:expr),+ => $inner:expr) => {
    155         Formatted::new(vec![$($fmts),+], $inner)
    156     };
    157 }
    158 
    159 #[cfg(test)]
    160 mod test {
    161     use super::{ColorKind, Formatted, FormattingKind};
    162 
    163     #[test]
    164     fn basic() {
    165         let f = Formatted::from_str("abc");
    166         assert_eq!(f.to_string(), String::from("abc"))
    167     }
    168 
    169     #[test]
    170     fn does_not_eat() {
    171         let f = irc_fmt!(FormattingKind::Color { fg: ColorKind::White, bg: None } => ",02");
    172         assert_ne!(f.to_string(), String::from("\u{0003}00,02\u{000F}"))
    173     }
    174 
    175     #[test]
    176     fn nested() {
    177         let f = irc_fmt!(FormattingKind::Bold, FormattingKind::ReverseColor => "abc");
    178         assert_eq!(f.to_string(), String::from("\u{0002}\u{0016}abc\u{000F}"))
    179     }
    180 }