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
9use std::fmt;
10use std::fs;
11use std::io::{self, Read};
12use std::path;
13
14use crate::reflection;
15use crate::utils;
16use crate::Predicate;
17
18fn read_file(path: &path::Path) -> io::Result<Vec<u8>> {
19 let mut buffer = Vec::new();
20 fs::File::open(path)?.read_to_end(&mut buffer)?;
21 Ok(buffer)
22}
23
24/// Predicate that compares file matches
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct BinaryFilePredicate {
27 path: path::PathBuf,
28 content: utils::DebugAdapter<Vec<u8>>,
29}
30
31impl BinaryFilePredicate {
32 fn eval(&self, path: &path::Path) -> io::Result<bool> {
33 let content = read_file(path)?;
34 Ok(self.content.debug == content)
35 }
36
37 /// Creates a new `Predicate` that ensures complete equality
38 ///
39 /// # Examples
40 ///
41 /// ```
42 /// use std::path::Path;
43 /// use predicates::prelude::*;
44 ///
45 /// let predicate_file = predicate::path::eq_file(Path::new("Cargo.toml")).utf8().unwrap();
46 /// assert_eq!(true, predicate_file.eval(Path::new("Cargo.toml")));
47 /// assert_eq!(false, predicate_file.eval(Path::new("Cargo.lock")));
48 /// assert_eq!(false, predicate_file.eval(Path::new("src")));
49 ///
50 /// assert_eq!(false, predicate_file.eval("Not a real Cargo.toml file content"));
51 /// ```
52 pub fn utf8(self) -> Option<StrFilePredicate> {
53 let path = self.path;
54 let content = String::from_utf8(self.content.debug).ok()?;
55 Some(StrFilePredicate { path, content })
56 }
57}
58
59impl Predicate<path::Path> for BinaryFilePredicate {
60 fn eval(&self, path: &path::Path) -> bool {
61 self.eval(path).unwrap_or(false)
62 }
63
64 fn find_case<'a>(
65 &'a self,
66 expected: bool,
67 variable: &path::Path,
68 ) -> Option<reflection::Case<'a>> {
69 utils::default_find_case(self, expected, variable)
70 }
71}
72
73impl Predicate<[u8]> for BinaryFilePredicate {
74 fn eval(&self, actual: &[u8]) -> bool {
75 self.content.debug == actual
76 }
77
78 fn find_case<'a>(&'a self, expected: bool, variable: &[u8]) -> Option<reflection::Case<'a>> {
79 utils::default_find_case(self, expected, variable)
80 }
81}
82
83impl reflection::PredicateReflection for BinaryFilePredicate {
84 fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> {
85 let params = vec![reflection::Parameter::new("content", &self.content)];
86 Box::new(params.into_iter())
87 }
88}
89
90impl fmt::Display for BinaryFilePredicate {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 let palette = crate::Palette::current();
93 write!(
94 f,
95 "{} {} {}",
96 palette.var.paint("var"),
97 palette.description.paint("is"),
98 palette.expected.paint(self.path.display())
99 )
100 }
101}
102
103/// Creates a new `Predicate` that ensures complete equality
104///
105/// # Examples
106///
107/// ```
108/// use std::path::Path;
109/// use predicates::prelude::*;
110///
111/// let predicate_file = predicate::path::eq_file(Path::new("Cargo.toml"));
112/// assert_eq!(true, predicate_file.eval(Path::new("Cargo.toml")));
113/// assert_eq!(false, predicate_file.eval(Path::new("src")));
114/// assert_eq!(false, predicate_file.eval(Path::new("src")));
115/// ```
116pub fn eq_file<P: Into<path::PathBuf>>(path: P) -> BinaryFilePredicate {
117 let path = path.into();
118 let content = utils::DebugAdapter::new(read_file(&path).unwrap());
119 BinaryFilePredicate { path, content }
120}
121
122/// Predicate that compares string content of files
123#[derive(Debug, Clone, PartialEq, Eq)]
124pub struct StrFilePredicate {
125 path: path::PathBuf,
126 content: String,
127}
128
129impl StrFilePredicate {
130 fn eval(&self, path: &path::Path) -> Option<bool> {
131 let content = read_file(path).ok()?;
132 let content = String::from_utf8(content).ok()?;
133 Some(self.content == content)
134 }
135}
136
137impl Predicate<path::Path> for StrFilePredicate {
138 fn eval(&self, path: &path::Path) -> bool {
139 self.eval(path).unwrap_or(false)
140 }
141
142 fn find_case<'a>(
143 &'a self,
144 expected: bool,
145 variable: &path::Path,
146 ) -> Option<reflection::Case<'a>> {
147 utils::default_find_case(self, expected, variable)
148 }
149}
150
151impl Predicate<str> for StrFilePredicate {
152 fn eval(&self, actual: &str) -> bool {
153 self.content == actual
154 }
155
156 fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> {
157 utils::default_find_case(self, expected, variable)
158 }
159}
160
161impl reflection::PredicateReflection for StrFilePredicate {
162 fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> {
163 let params = vec![reflection::Parameter::new("content", &self.content)];
164 Box::new(params.into_iter())
165 }
166}
167
168impl fmt::Display for StrFilePredicate {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 let palette = crate::Palette::current();
171 write!(
172 f,
173 "{} {} {}",
174 palette.var.paint("var"),
175 palette.description.paint("is"),
176 palette.expected.paint(self.path.display())
177 )
178 }
179}
180