1 | // Copyright (c) 2018 The predicates-rs Project Developers. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
4 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
5 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
6 | // option. This file may not be copied, modified, or distributed |
7 | // except according to those terms. |
8 | |
9 | use std::borrow; |
10 | use std::fmt; |
11 | |
12 | use crate::reflection; |
13 | use crate::Predicate; |
14 | |
15 | /// Predicate that diffs two strings. |
16 | /// |
17 | /// This is created by the `predicate::str::diff`. |
18 | #[derive(Debug, Clone, PartialEq, Eq)] |
19 | pub struct DifferencePredicate { |
20 | orig: borrow::Cow<'static, str>, |
21 | } |
22 | |
23 | impl Predicate<str> for DifferencePredicate { |
24 | fn eval(&self, edit: &str) -> bool { |
25 | edit == self.orig |
26 | } |
27 | |
28 | fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> { |
29 | let result = variable != self.orig; |
30 | if result == expected { |
31 | None |
32 | } else { |
33 | let palette = crate::Palette::current(); |
34 | let orig: Vec<_> = self.orig.lines().map(|l| format!("{} \n" , l)).collect(); |
35 | let variable: Vec<_> = variable.lines().map(|l| format!("{} \n" , l)).collect(); |
36 | let diff = difflib::unified_diff( |
37 | &orig, |
38 | &variable, |
39 | "" , |
40 | "" , |
41 | &palette.expected.paint("orig" ).to_string(), |
42 | &palette.var.paint("var" ).to_string(), |
43 | 0, |
44 | ); |
45 | let mut diff = colorize_diff(diff, palette); |
46 | diff.insert(0, " \n" .to_owned()); |
47 | |
48 | Some( |
49 | reflection::Case::new(Some(self), result).add_product(reflection::Product::new( |
50 | "diff" , |
51 | itertools::join(diff.iter(), "" ), |
52 | )), |
53 | ) |
54 | } |
55 | } |
56 | } |
57 | |
58 | impl reflection::PredicateReflection for DifferencePredicate { |
59 | fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> { |
60 | let params = vec![reflection::Parameter::new("original" , &self.orig)]; |
61 | Box::new(params.into_iter()) |
62 | } |
63 | } |
64 | |
65 | impl fmt::Display for DifferencePredicate { |
66 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
67 | let palette = crate::Palette::current(); |
68 | write!( |
69 | f, |
70 | "{} {} {}" , |
71 | palette.description.paint("diff" ), |
72 | palette.expected.paint("original" ), |
73 | palette.var.paint("var" ), |
74 | ) |
75 | } |
76 | } |
77 | |
78 | /// Creates a new `Predicate` that diffs two strings. |
79 | /// |
80 | /// # Examples |
81 | /// |
82 | /// ``` |
83 | /// use predicates::prelude::*; |
84 | /// |
85 | /// let predicate_fn = predicate::str::diff("Hello World" ); |
86 | /// assert_eq!(true, predicate_fn.eval("Hello World" )); |
87 | /// assert!(predicate_fn.find_case(false, "Hello World" ).is_none()); |
88 | /// assert_eq!(false, predicate_fn.eval("Goodbye World" )); |
89 | /// assert!(predicate_fn.find_case(false, "Goodbye World" ).is_some()); |
90 | /// ``` |
91 | pub fn diff<S>(orig: S) -> DifferencePredicate |
92 | where |
93 | S: Into<borrow::Cow<'static, str>>, |
94 | { |
95 | DifferencePredicate { orig: orig.into() } |
96 | } |
97 | |
98 | #[cfg (feature = "color" )] |
99 | fn colorize_diff(mut lines: Vec<String>, palette: crate::Palette) -> Vec<String> { |
100 | for (i, line) in lines.iter_mut().enumerate() { |
101 | match (i, line.as_bytes().get(0)) { |
102 | (0, _) => { |
103 | if let Some((prefix, body)) = line.split_once(' ' ) { |
104 | *line = format!("{} {}" , palette.expected.paint(prefix), body); |
105 | } |
106 | } |
107 | (1, _) => { |
108 | if let Some((prefix, body)) = line.split_once(' ' ) { |
109 | *line = format!("{} {}" , palette.var.paint(prefix), body); |
110 | } |
111 | } |
112 | (_, Some(b'-' )) => { |
113 | let (prefix, body) = line.split_at(1); |
114 | *line = format!("{}{}" , palette.expected.paint(prefix), body); |
115 | } |
116 | (_, Some(b'+' )) => { |
117 | let (prefix, body) = line.split_at(1); |
118 | *line = format!("{}{}" , palette.var.paint(prefix), body); |
119 | } |
120 | (_, Some(b'@' )) => { |
121 | *line = format!("{}" , palette.description.paint(&line)); |
122 | } |
123 | _ => (), |
124 | } |
125 | } |
126 | lines |
127 | } |
128 | |
129 | #[cfg (not(feature = "color" ))] |
130 | fn colorize_diff(lines: Vec<String>, _palette: crate::Palette) -> Vec<String> { |
131 | lines |
132 | } |
133 | |