#[cfg(test)]
use proptest::collection::vec;
#[cfg(test)]
use proptest::prelude::*;
use std::fmt;
use std::iter::{Extend, FromIterator, IntoIterator};
#[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<Value = Name> {
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 {
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
Black,
RGB(u8, u8, u8),
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Style {
Reset,
Bold,
Underline,
Italic,
Fg(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<Value = Style> {
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))),
]
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct CompleteStyle {
pub fg: Option<Color>,
pub bg: Option<Color>,
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<Style> for CompleteStyle {
fn from(s: Style) -> Self {
let mut ctx = Self::default();
ctx.add(s);
ctx
}
}
impl fmt::Display for CompleteStyle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Style::*;
if *self == Self::default() {
return write!(f, "{}", Reset);
}
if let Some(color) = self.fg {
write!(f, "{}", Fg(color))?;
}
if let Some(color) = self.bg {
write!(f, "{}", Bg(color))?;
}
if self.bold {
write!(f, "{}", Bold)?;
}
if self.italics {
write!(f, "{}", Italic)?;
}
if self.underline {
write!(f, "{}", Underline)?;
}
Ok(())
}
}
impl<'a> Extend<&'a Style> for CompleteStyle {
fn extend<E: IntoIterator<Item = &'a Style>>(&mut self, styles: E) {
for style in styles {
self.add(*style)
}
}
}
impl<'a> FromIterator<&'a Style> for CompleteStyle {
fn from_iter<I: IntoIterator<Item = &'a Style>>(iter: I) -> CompleteStyle {
let mut complete = CompleteStyle::default();
complete.extend(iter);
complete
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Delimiter {
Angle,
Square,
Curly,
Parens,
}
impl Delimiter {
pub fn left(&self) -> &'static str {
use Delimiter::*;
match self {
Angle => "<",
Square => "[",
Curly => "{",
Parens => "(",
}
}
pub fn right(&self) -> &'static str {
use Delimiter::*;
match self {
Angle => ">",
Square => "]",
Curly => "}",
Parens => ")",
}
}
}
#[cfg(test)]
pub fn arb_delimiter() -> impl Strategy<Value = Delimiter> {
use self::Delimiter::*;
prop_oneof![Just(Angle), Just(Square), Just(Curly), Just(Parens)]
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Separator {
At,
Bar,
Dot,
Comma,
Space,
Colon,
Semicolon,
Underscore,
}
impl Separator {
pub fn as_str(&self) -> &'static str {
use Separator::*;
match self {
At => "@",
Bar => "|",
Dot => ".",
Comma => ",",
Space => " ",
Colon => ":",
Semicolon => ";",
Underscore => "_",
}
}
}
impl AsRef<str> for Separator {
fn as_ref(&self) -> &'static str {
self.as_str()
}
}
impl fmt::Display for Separator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_ref())
}
}
#[cfg(test)]
pub fn arb_separator() -> impl Strategy<Value = Separator> {
use Separator::*;
prop_oneof![
Just(At),
Just(Bar),
Just(Dot),
Just(Comma),
Just(Space),
Just(Colon),
Just(Semicolon),
Just(Underscore),
]
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Expression {
Named {
name: Name,
sub: Tree,
},
Format { style: CompleteStyle, sub: Tree },
Group {
d: Delimiter,
sub: Tree,
},
Literal(String),
Separator(Separator),
}
impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expression::Named { ref name, ref sub } => {
write!(f, "{}", name)?;
if sub.0.is_empty() {
Ok(())
} else {
write!(f, "({})", sub)
}
}
Expression::Group { ref d, ref sub } => match d {
Delimiter::Square => write!(f, "[{}]", sub),
Delimiter::Angle => write!(f, "<{}>", sub),
Delimiter::Parens => write!(f, "\\({})", sub),
Delimiter::Curly => write!(f, "{{{}}}", sub),
},
Expression::Format { ref style, ref sub } => {
write!(f, "#")?;
write!(f, "{}", style)?;
write!(f, "({})", sub)
}
Expression::Literal(ref string) => write!(f, "'{}'", string),
Expression::Separator(s) => write!(f, "{}", s),
}
}
}
#[cfg(test)]
pub fn arb_expression() -> impl Strategy<Value = Expression> {
use self::Expression::*;
let leaf = prop_oneof![
arb_name().prop_map(|name| Named {
name: name,
sub: Tree::new(),
}),
vec(arb_style(), 1..5).prop_map(|style| Format {
style: style.iter().collect(),
sub: Tree::new(),
}),
"[^']*".prop_map(Literal),
arb_separator().prop_map(Separator),
];
leaf.prop_recursive(8, 64, 10, |inner| {
prop_oneof![
(arb_name(), vec(inner.clone(), 0..10)).prop_map(|(name, sub)| Named {
name: name,
sub: Tree(sub),
}),
(vec(arb_style(), 1..10), vec(inner.clone(), 0..10)).prop_map(|(style, sub)| Format {
style: style.iter().collect(),
sub: Tree(sub),
}),
(arb_delimiter(), vec(inner.clone(), 0..10)).prop_map(|(delimiter, sub)| Group {
d: delimiter,
sub: Tree(sub),
}),
arb_separator().prop_map(Separator),
]
})
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Tree(pub Vec<Expression>);
impl Tree {
pub fn new() -> Tree {
Tree(Vec::new())
}
}
impl Default for Tree {
fn default() -> Self {
Tree(Vec::new())
}
}
impl fmt::Display for Tree {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for exp in &self.0 {
write!(f, "{}", exp)?;
}
Ok(())
}
}
#[cfg(test)]
pub fn arb_tree(n: usize) -> impl Strategy<Value = Tree> {
vec(arb_expression(), 0..n).prop_map(Tree)
}