1#[cfg(test)]
2use proptest::collection::vec;
3#[cfg(test)]
4use proptest::prelude::*;
5use std::fmt;
6use std::iter::{Extend, FromIterator, IntoIterator};
7
8#[derive(Debug, PartialEq, Eq, Copy, Clone)]
12pub enum Name {
13 Branch,
14 Remote,
15 Ahead,
16 Behind,
17 Conflict,
18 Added,
19 Untracked,
20 Modified,
21 Unstaged,
22 Deleted,
23 DeletedStaged,
24 Renamed,
25 Stashed,
26 Quote,
27}
28
29impl fmt::Display for Name {
30 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31 let literal = match self {
32 Name::Stashed => "h",
33 Name::Branch => "b",
34 Name::Remote => "B",
35 Name::Ahead => "+",
36 Name::Behind => "-",
37 Name::Conflict => "u",
38 Name::Added => "A",
39 Name::Untracked => "a",
40 Name::Modified => "M",
41 Name::Unstaged => "m",
42 Name::Deleted => "d",
43 Name::DeletedStaged => "D",
44 Name::Renamed => "R",
45 Name::Quote => "\\\'",
46 };
47 write!(f, "{}", literal)
48 }
49}
50
51#[cfg(test)]
52pub fn arb_name() -> impl Strategy<Value = Name> {
53 use self::Name::*;
54
55 prop_oneof![
56 Just(Branch),
57 Just(Remote),
58 Just(Ahead),
59 Just(Behind),
60 Just(Conflict),
61 Just(Added),
62 Just(Untracked),
63 Just(Modified),
64 Just(Unstaged),
65 Just(Deleted),
66 Just(DeletedStaged),
67 Just(Renamed),
68 Just(Stashed),
69 Just(Quote),
70 ]
71}
72
73#[derive(Debug, PartialEq, Eq, Copy, Clone)]
74pub enum Color {
75 Red,
77 Green,
79 Yellow,
81 Blue,
83 Magenta,
85 Cyan,
87 White,
89 Black,
91 RGB(u8, u8, u8),
93}
94
95#[derive(Debug, PartialEq, Eq, Copy, Clone)]
99pub enum Style {
100 Reset,
102 Bold,
104 Underline,
106 Italic,
108 Fg(Color),
110 Bg(Color),
112}
113
114impl fmt::Display for Style {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 use Color::*;
117 match self {
118 Style::Reset => write!(f, "~")?,
119 Style::Bold => write!(f, "*")?,
120 Style::Underline => write!(f, "_")?,
121 Style::Italic => write!(f, "i")?,
122 Style::Fg(Red) => write!(f, "r")?,
123 Style::Bg(Red) => write!(f, "R")?,
124 Style::Fg(Green) => write!(f, "g")?,
125 Style::Bg(Green) => write!(f, "G")?,
126 Style::Fg(Yellow) => write!(f, "y")?,
127 Style::Bg(Yellow) => write!(f, "Y")?,
128 Style::Fg(Blue) => write!(f, "b")?,
129 Style::Bg(Blue) => write!(f, "B")?,
130 Style::Fg(Magenta) => write!(f, "m")?,
131 Style::Bg(Magenta) => write!(f, "M")?,
132 Style::Fg(Cyan) => write!(f, "c")?,
133 Style::Bg(Cyan) => write!(f, "C")?,
134 Style::Fg(White) => write!(f, "w")?,
135 Style::Bg(White) => write!(f, "W")?,
136 Style::Fg(Black) => write!(f, "k")?,
137 Style::Bg(Black) => write!(f, "K")?,
138 &Style::Fg(RGB(r, g, b)) => write!(f, "[{},{},{}]", r, g, b)?,
139 &Style::Bg(RGB(r, g, b)) => write!(f, "{{{},{},{}}}", r, g, b)?,
140 };
141 Ok(())
142 }
143}
144
145#[cfg(test)]
146pub fn arb_style() -> impl Strategy<Value = Style> {
147 use self::Color::*;
148 use self::Style::*;
149
150 prop_oneof![
151 Just(Reset),
152 Just(Bold),
153 Just(Underline),
154 Just(Italic),
155 Just(Fg(Red)),
156 Just(Bg(Red)),
157 Just(Fg(Green)),
158 Just(Bg(Green)),
159 Just(Fg(Yellow)),
160 Just(Bg(Yellow)),
161 Just(Fg(Blue)),
162 Just(Bg(Blue)),
163 Just(Fg(Magenta)),
164 Just(Bg(Magenta)),
165 Just(Fg(Cyan)),
166 Just(Bg(Cyan)),
167 Just(Fg(White)),
168 Just(Bg(White)),
169 Just(Fg(Black)),
170 Just(Bg(Black)),
171 any::<(u8, u8, u8)>().prop_map(|(r, g, b)| Fg(RGB(r, g, b))),
172 any::<(u8, u8, u8)>().prop_map(|(r, g, b)| Bg(RGB(r, g, b))),
173 ]
174}
175
176#[derive(Debug, Copy, Clone, PartialEq, Eq)]
208pub struct CompleteStyle {
209 pub fg: Option<Color>,
210 pub bg: Option<Color>,
211 pub bold: bool,
212 pub italics: bool,
213 pub underline: bool,
214}
215
216impl CompleteStyle {
217 pub fn add(&mut self, style: Style) {
218 use Style::*;
219 match style {
220 Fg(color) => self.fg = Some(color),
221 Bg(color) => self.bg = Some(color),
222 Bold => self.bold = true,
223 Italic => self.italics = true,
224 Underline => self.underline = true,
225 Reset => *self = Default::default(),
226 }
227 }
228}
229
230impl Default for CompleteStyle {
231 fn default() -> Self {
232 CompleteStyle {
233 fg: None,
234 bg: None,
235 bold: false,
236 italics: false,
237 underline: false,
238 }
239 }
240}
241
242impl std::ops::AddAssign for CompleteStyle {
243 fn add_assign(&mut self, with: Self) {
244 if with == Default::default() {
245 return *self = Default::default();
246 }
247
248 *self = Self {
249 fg: with.fg.or(self.fg),
250 bg: with.bg.or(self.bg),
251 bold: with.bold || self.bold,
252 italics: with.italics || self.italics,
253 underline: with.underline || self.underline,
254 }
255 }
256}
257
258impl From<Style> for CompleteStyle {
259 fn from(s: Style) -> Self {
260 let mut ctx = Self::default();
261 ctx.add(s);
262 ctx
263 }
264}
265
266impl fmt::Display for CompleteStyle {
267 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268 use Style::*;
269
270 if *self == Self::default() {
271 return write!(f, "{}", Reset);
272 }
273
274 if let Some(color) = self.fg {
275 write!(f, "{}", Fg(color))?;
276 }
277 if let Some(color) = self.bg {
278 write!(f, "{}", Bg(color))?;
279 }
280 if self.bold {
281 write!(f, "{}", Bold)?;
282 }
283 if self.italics {
284 write!(f, "{}", Italic)?;
285 }
286 if self.underline {
287 write!(f, "{}", Underline)?;
288 }
289
290 Ok(())
291 }
292}
293
294impl<'a> Extend<&'a Style> for CompleteStyle {
295 fn extend<E: IntoIterator<Item = &'a Style>>(&mut self, styles: E) {
296 for style in styles {
297 self.add(*style)
298 }
299 }
300}
301
302impl<'a> FromIterator<&'a Style> for CompleteStyle {
303 fn from_iter<I: IntoIterator<Item = &'a Style>>(iter: I) -> CompleteStyle {
304 let mut complete = CompleteStyle::default();
305 complete.extend(iter);
306 complete
307 }
308}
309
310#[derive(Debug, PartialEq, Eq, Clone, Copy)]
311pub enum Delimiter {
312 Angle,
314 Square,
316 Curly,
318 Parens,
320}
321
322impl Delimiter {
323 pub fn left(&self) -> &'static str {
324 use Delimiter::*;
325 match self {
326 Angle => "<",
327 Square => "[",
328 Curly => "{",
329 Parens => "(",
330 }
331 }
332
333 pub fn right(&self) -> &'static str {
334 use Delimiter::*;
335 match self {
336 Angle => ">",
337 Square => "]",
338 Curly => "}",
339 Parens => ")",
340 }
341 }
342}
343
344#[cfg(test)]
345pub fn arb_delimiter() -> impl Strategy<Value = Delimiter> {
346 use self::Delimiter::*;
347
348 prop_oneof![Just(Angle), Just(Square), Just(Curly), Just(Parens)]
349}
350
351#[derive(Debug, Copy, Clone, Eq, PartialEq)]
353pub enum Separator {
354 At,
355 Bar,
356 Dot,
357 Comma,
358 Space,
359 Colon,
360 Semicolon,
361 Underscore,
362}
363
364impl Separator {
365 pub fn as_str(&self) -> &'static str {
366 use Separator::*;
367 match self {
368 At => "@",
369 Bar => "|",
370 Dot => ".",
371 Comma => ",",
372 Space => " ",
373 Colon => ":",
374 Semicolon => ";",
375 Underscore => "_",
376 }
377 }
378}
379
380impl AsRef<str> for Separator {
381 fn as_ref(&self) -> &'static str {
382 self.as_str()
383 }
384}
385
386impl fmt::Display for Separator {
387 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
388 write!(f, "{}", self.as_ref())
389 }
390}
391
392#[cfg(test)]
393pub fn arb_separator() -> impl Strategy<Value = Separator> {
394 use Separator::*;
395
396 prop_oneof![
397 Just(At),
398 Just(Bar),
399 Just(Dot),
400 Just(Comma),
401 Just(Space),
402 Just(Colon),
403 Just(Semicolon),
404 Just(Underscore),
405 ]
406}
407
408#[derive(Debug, PartialEq, Eq, Clone)]
441pub enum Expression {
442 Named {
444 name: Name,
446 sub: Tree,
448 },
449 Format { style: CompleteStyle, sub: Tree },
451 Group {
453 d: Delimiter,
455 sub: Tree,
457 },
458 Literal(String),
460 Separator(Separator),
462}
463
464impl fmt::Display for Expression {
465 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466 match self {
467 Expression::Named { ref name, ref sub } => {
468 write!(f, "{}", name)?;
469 if sub.0.is_empty() {
470 Ok(())
471 } else {
472 write!(f, "({})", sub)
473 }
474 }
475 Expression::Group { ref d, ref sub } => match d {
476 Delimiter::Square => write!(f, "[{}]", sub),
477 Delimiter::Angle => write!(f, "<{}>", sub),
478 Delimiter::Parens => write!(f, "\\({})", sub),
479 Delimiter::Curly => write!(f, "{{{}}}", sub),
480 },
481 Expression::Format { ref style, ref sub } => {
482 write!(f, "#")?;
483 write!(f, "{}", style)?;
484 write!(f, "({})", sub)
485 }
486 Expression::Literal(ref string) => write!(f, "'{}'", string),
487 Expression::Separator(s) => write!(f, "{}", s),
488 }
489 }
490}
491
492#[cfg(test)]
493pub fn arb_expression() -> impl Strategy<Value = Expression> {
494 use self::Expression::*;
495
496 let leaf = prop_oneof![
497 arb_name().prop_map(|name| Named {
498 name: name,
499 sub: Tree::new(),
500 }),
501 vec(arb_style(), 1..5).prop_map(|style| Format {
502 style: style.iter().collect(),
503 sub: Tree::new(),
504 }),
505 "[^']*".prop_map(Literal),
506 arb_separator().prop_map(Separator),
507 ];
508
509 leaf.prop_recursive(8, 64, 10, |inner| {
510 prop_oneof![
511 (arb_name(), vec(inner.clone(), 0..10)).prop_map(|(name, sub)| Named {
512 name: name,
513 sub: Tree(sub),
514 }),
515 (vec(arb_style(), 1..10), vec(inner.clone(), 0..10)).prop_map(|(style, sub)| Format {
516 style: style.iter().collect(),
517 sub: Tree(sub),
518 }),
519 (arb_delimiter(), vec(inner.clone(), 0..10)).prop_map(|(delimiter, sub)| Group {
520 d: delimiter,
521 sub: Tree(sub),
522 }),
523 arb_separator().prop_map(Separator),
524 ]
525 })
526}
527
528#[derive(Debug, PartialEq, Eq, Clone)]
533pub struct Tree(pub Vec<Expression>);
534
535impl Tree {
536 pub fn new() -> Tree {
538 Tree(Vec::new())
539 }
540}
541
542impl Default for Tree {
543 fn default() -> Self {
544 Tree(Vec::new())
545 }
546}
547
548impl fmt::Display for Tree {
549 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
550 for exp in &self.0 {
551 write!(f, "{}", exp)?;
552 }
553 Ok(())
554 }
555}
556
557#[cfg(test)]
558pub fn arb_tree(n: usize) -> impl Strategy<Value = Tree> {
559 vec(arb_expression(), 0..n).prop_map(Tree)
560}