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::fmt;
10 use std::fs;
11 use std::io::{self, Read};
12 use std::path;
13 
14 use crate::reflection;
15 use crate::utils;
16 use crate::Predicate;
17 
read_file(path: &path::Path) -> io::Result<Vec<u8>>18 fn 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)]
26 pub struct BinaryFilePredicate {
27     path: path::PathBuf,
28     content: utils::DebugAdapter<Vec<u8>>,
29 }
30 
31 impl BinaryFilePredicate {
eval(&self, path: &path::Path) -> io::Result<bool>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     /// ```
utf8(self) -> Option<StrFilePredicate>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 
59 impl Predicate<path::Path> for BinaryFilePredicate {
eval(&self, path: &path::Path) -> bool60     fn eval(&self, path: &path::Path) -> bool {
61         self.eval(path).unwrap_or(false)
62     }
63 
find_case<'a>( &'a self, expected: bool, variable: &path::Path, ) -> Option<reflection::Case<'a>>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 
73 impl Predicate<[u8]> for BinaryFilePredicate {
eval(&self, actual: &[u8]) -> bool74     fn eval(&self, actual: &[u8]) -> bool {
75         self.content.debug == actual
76     }
77 
find_case<'a>(&'a self, expected: bool, variable: &[u8]) -> Option<reflection::Case<'a>>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 
83 impl reflection::PredicateReflection for BinaryFilePredicate {
parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a>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 
90 impl fmt::Display for BinaryFilePredicate {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result91     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92         let palette = crate::Palette::new(f.alternate());
93         write!(
94             f,
95             "{} {} {}",
96             palette.var("var"),
97             palette.description("is"),
98             palette.expected(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 /// ```
eq_file<P: Into<path::PathBuf>>(path: P) -> BinaryFilePredicate116 pub 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)]
124 pub struct StrFilePredicate {
125     path: path::PathBuf,
126     content: String,
127 }
128 
129 impl StrFilePredicate {
eval(&self, path: &path::Path) -> Option<bool>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 
137 impl Predicate<path::Path> for StrFilePredicate {
eval(&self, path: &path::Path) -> bool138     fn eval(&self, path: &path::Path) -> bool {
139         self.eval(path).unwrap_or(false)
140     }
141 
find_case<'a>( &'a self, expected: bool, variable: &path::Path, ) -> Option<reflection::Case<'a>>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 
151 impl Predicate<str> for StrFilePredicate {
eval(&self, actual: &str) -> bool152     fn eval(&self, actual: &str) -> bool {
153         self.content == actual
154     }
155 
find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>>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 
161 impl reflection::PredicateReflection for StrFilePredicate {
parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a>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 
168 impl fmt::Display for StrFilePredicate {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result169     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170         let palette = crate::Palette::new(f.alternate());
171         write!(
172             f,
173             "{} {} {}",
174             palette.var("var"),
175             palette.description("is"),
176             palette.expected(self.path.display())
177         )
178     }
179 }
180