#[cfg(test)] use proptest::collection::vec; #[cfg(test)] use proptest::prelude::*; use std::fmt; use std::iter::{Extend, FromIterator, IntoIterator}; /// All valid expression names /// /// Defines the "standard library" of named expressions which represent git stats #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Name { Branch, Remote, Ahead, Behind, Conflict, Added, Untracked, Modified, Unstaged, Deleted, DeletedStaged, Renamed, Stashed, Quote, } impl fmt::Display for Name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let literal = match self { Name::Stashed => "h", Name::Branch => "b", Name::Remote => "B", Name::Ahead => "+", Name::Behind => "-", Name::Conflict => "u", Name::Added => "A", Name::Untracked => "a", Name::Modified => "M", Name::Unstaged => "m", Name::Deleted => "d", Name::DeletedStaged => "D", Name::Renamed => "R", Name::Quote => "\\\'", }; write!(f, "{}", literal) } } #[cfg(test)] pub fn arb_name() -> impl Strategy { use self::Name::*; prop_oneof![ Just(Branch), Just(Remote), Just(Ahead), Just(Behind), Just(Conflict), Just(Added), Just(Untracked), Just(Modified), Just(Unstaged), Just(Deleted), Just(DeletedStaged), Just(Renamed), Just(Stashed), Just(Quote), ] } #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Color { /// Make text red Red, /// Make text green Green, /// Make the text yellow Yellow, /// Make the text blue Blue, /// Make the text purple Magenta, /// Make the text cyan Cyan, /// Make the text white White, /// Make the text bright black Black, /// Provide a 256 color table text color value RGB(u8, u8, u8), } /// All valid style markers /// /// Defines the range of possible styles #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Style { /// Reset text to plain terminal style; ANSI code 00 equivalent Reset, /// Bold text in the terminal; ANSI code 01 equivalent Bold, /// Underline text in the terminal; ANSI code 04 equivalent Underline, /// Italisize text in the terminal; ANSI code 03 equivalent Italic, /// Set a foreground color Fg(Color), /// Set a background color Bg(Color), } impl fmt::Display for Style { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Color::*; match self { Style::Reset => write!(f, "~")?, Style::Bold => write!(f, "*")?, Style::Underline => write!(f, "_")?, Style::Italic => write!(f, "i")?, Style::Fg(Red) => write!(f, "r")?, Style::Bg(Red) => write!(f, "R")?, Style::Fg(Green) => write!(f, "g")?, Style::Bg(Green) => write!(f, "G")?, Style::Fg(Yellow) => write!(f, "y")?, Style::Bg(Yellow) => write!(f, "Y")?, Style::Fg(Blue) => write!(f, "b")?, Style::Bg(Blue) => write!(f, "B")?, Style::Fg(Magenta) => write!(f, "m")?, Style::Bg(Magenta) => write!(f, "M")?, Style::Fg(Cyan) => write!(f, "c")?, Style::Bg(Cyan) => write!(f, "C")?, Style::Fg(White) => write!(f, "w")?, Style::Bg(White) => write!(f, "W")?, Style::Fg(Black) => write!(f, "k")?, Style::Bg(Black) => write!(f, "K")?, &Style::Fg(RGB(r, g, b)) => write!(f, "[{},{},{}]", r, g, b)?, &Style::Bg(RGB(r, g, b)) => write!(f, "{{{},{},{}}}", r, g, b)?, }; Ok(()) } } #[cfg(test)] pub fn arb_style() -> impl Strategy { use self::Color::*; use self::Style::*; prop_oneof![ Just(Reset), Just(Bold), Just(Underline), Just(Italic), Just(Fg(Red)), Just(Bg(Red)), Just(Fg(Green)), Just(Bg(Green)), Just(Fg(Yellow)), Just(Bg(Yellow)), Just(Fg(Blue)), Just(Bg(Blue)), Just(Fg(Magenta)), Just(Bg(Magenta)), Just(Fg(Cyan)), Just(Bg(Cyan)), Just(Fg(White)), Just(Bg(White)), Just(Fg(Black)), Just(Bg(Black)), any::<(u8, u8, u8)>().prop_map(|(r, g, b)| Fg(RGB(r, g, b))), any::<(u8, u8, u8)>().prop_map(|(r, g, b)| Bg(RGB(r, g, b))), ] } /// An aggregate unit which describes the sub total of a set of styles /// /// ``` /// use glitter_lang::ast::{Style, CompleteStyle, Color}; /// use Style::*; /// use Color::*; /// let complete: CompleteStyle = [Fg(Green), Bold].iter().collect(); /// assert_eq!(complete, CompleteStyle { /// fg: Some(Green), /// bold: true, /// ..Default::default() /// }); /// ``` /// /// The conversion from a collection of styles is lossy: /// /// ``` /// use glitter_lang::ast::{Style, CompleteStyle, Color}; /// use Style::*; /// use Color::*; /// /// // Style::Reset at the final position is the same as /// // CompleteStyle::default() /// let reset_to_default: CompleteStyle = [Bg(Red), Reset].iter().collect(); /// assert_eq!(reset_to_default, CompleteStyle::default()); /// /// // Information about repeated styles is lost /// let green: CompleteStyle = [Fg(Green)].iter().collect(); /// let green_repeat: CompleteStyle = std::iter::repeat(&Fg(Green)).take(10).collect(); /// assert_eq!(green, green_repeat); /// ``` #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct CompleteStyle { pub fg: Option, pub bg: Option, pub bold: bool, pub italics: bool, pub underline: bool, } impl CompleteStyle { pub fn add(&mut self, style: Style) { use Style::*; match style { Fg(color) => self.fg = Some(color), Bg(color) => self.bg = Some(color), Bold => self.bold = true, Italic => self.italics = true, Underline => self.underline = true, Reset => *self = Default::default(), } } } impl Default for CompleteStyle { fn default() -> Self { CompleteStyle { fg: None, bg: None, bold: false, italics: false, underline: false, } } } impl std::ops::AddAssign for CompleteStyle { fn add_assign(&mut self, with: Self) { if with == Default::default() { return *self = Default::default(); } *self = Self { fg: with.fg.or(self.fg), bg: with.bg.or(self.bg), bold: with.bold || self.bold, italics: with.italics || self.italics, underline: with.underline || self.underline, } } } impl From