1use super::{AssertionFailure, DescriptiveSpec, Spec};
2
3use std::borrow::Borrow;
4use std::path::Path;
5
6pub trait PathAssertions {
7 #[track_caller]
8 fn exists(&mut self);
9 #[track_caller]
10 fn does_not_exist(&mut self);
11 #[track_caller]
12 fn is_a_file(&mut self);
13 #[track_caller]
14 fn is_a_directory(&mut self);
15 #[track_caller]
16 fn has_file_name<'r, E: Borrow<&'r str>>(&mut self, expected_file_name: E);
17}
18
19impl<T> PathAssertions for Spec<'_, T>
20where
21 T: AsRef<Path>,
22{
23 fn exists(&mut self) {
29 exists(self.subject.as_ref(), self)
30 }
31
32 fn does_not_exist(&mut self) {
40 does_not_exist(self.subject.as_ref(), self)
41 }
42
43 fn is_a_file(&mut self) {
49 is_a_file(self.subject.as_ref(), self)
50 }
51
52 fn is_a_directory(&mut self) {
58 is_a_directory(self.subject.as_ref(), self)
59 }
60
61 fn has_file_name<'r, E: Borrow<&'r str>>(&mut self, expected_file_name: E) {
69 has_file_name(self.subject.as_ref(), expected_file_name.borrow(), self)
70 }
71}
72
73fn exists<'s, S: DescriptiveSpec<'s>>(subject: &Path, spec: &'s S) {
74 if !subject.exists() {
75 AssertionFailure::from_spec(spec)
76 .with_expected(format!("Path of <{:?}> to exist", subject))
77 .with_actual("a non-existent Path".to_string())
78 .fail();
79 }
80}
81
82fn does_not_exist<'s, S: DescriptiveSpec<'s>>(subject: &Path, spec: &'s S) {
83 if subject.exists() {
84 AssertionFailure::from_spec(spec)
85 .with_expected(format!("Path of <{:?}> to not exist", subject))
86 .with_actual("a resolvable Path".to_string())
87 .fail();
88 }
89}
90
91fn is_a_file<'s, S: DescriptiveSpec<'s>>(subject: &Path, spec: &'s S) {
92 if !subject.is_file() {
93 AssertionFailure::from_spec(spec)
94 .with_expected(format!("Path of <{:?}> to be a file", subject))
95 .with_actual("not a resolvable file".to_string())
96 .fail();
97 }
98}
99
100fn is_a_directory<'s, S: DescriptiveSpec<'s>>(subject: &Path, spec: &'s S) {
101 if !subject.is_dir() {
102 AssertionFailure::from_spec(spec)
103 .with_expected(format!("Path of <{:?}> to be a directory", subject))
104 .with_actual("not a resolvable directory".to_string())
105 .fail();
106 }
107}
108
109fn has_file_name<'s, S: DescriptiveSpec<'s>>(
110 subject: &Path,
111 expected_file_name: &str,
112 spec: &'s S,
113) {
114 let subject_file_name = match subject.file_name() {
115 Some(os_string) => match os_string.to_str() {
116 Some(val) => val,
117 None => {
118 fail_from_file_name(
119 spec,
120 expected_file_name,
121 "an invalid UTF-8 file name".to_string(),
122 );
123 unreachable!();
124 }
125 },
126 None => {
127 fail_from_file_name(
128 spec,
129 expected_file_name,
130 format!("a non-resolvable path <{:?}>", subject),
131 );
132 unreachable!();
133 }
134 };
135
136 if !subject_file_name.eq(expected_file_name) {
137 fail_from_file_name(spec, expected_file_name, format!("<{}>", subject_file_name));
138 }
139}
140
141fn fail_from_file_name<'s, S: DescriptiveSpec<'s>>(spec: &'s S, expected: &str, actual: String) {
142 AssertionFailure::from_spec(spec)
143 .with_expected(build_file_name_message(expected))
144 .with_actual(actual)
145 .fail();
146}
147
148fn build_file_name_message(file_name: &str) -> String {
149 format!("Path with file name of <{}>", file_name)
150}
151
152#[cfg(test)]
153mod tests {
154 #![allow(clippy::needless_borrows_for_generic_args)]
155 use super::super::prelude::*;
156
157 use std::path::{Path, PathBuf};
158
159 static MANIFEST_PATH: &str = env!("CARGO_MANIFEST_DIR");
160
161 #[test]
162 fn should_accept_any_path_reference() {
163 assert_that(&Path::new(MANIFEST_PATH)).exists();
164 assert_that(&PathBuf::from(MANIFEST_PATH)).exists();
165 assert_that(&MANIFEST_PATH).exists();
166 }
167
168 #[test]
169 pub fn should_not_panic_if_path_exists() {
170 assert_that(&Path::new(MANIFEST_PATH)).exists();
171 }
172
173 #[test]
174 #[should_panic]
176 pub fn should_panic_if_path_does_not_exist() {
177 let failing_path = MANIFEST_PATH.to_string() + "/does-not-exist";
178 assert_that(&Path::new(&failing_path)).exists();
179 }
180
181 #[test]
182 pub fn should_not_panic_if_path_represents_a_directory() {
183 assert_that(&Path::new(MANIFEST_PATH)).is_a_directory();
184 }
185
186 #[test]
187 pub fn should_not_panic_if_path_does_not_exist_when_expected() {
188 let failing_path = MANIFEST_PATH.to_string() + "/does-not-exist";
189 assert_that(&Path::new(&failing_path)).does_not_exist();
190 }
191
192 #[test]
193 #[should_panic]
195 pub fn should_panic_if_path_exists_when_not_expected() {
196 assert_that(&Path::new(MANIFEST_PATH)).does_not_exist();
197 }
198
199 #[test]
200 #[should_panic]
202 pub fn should_panic_if_path_does_not_represent_a_directory() {
203 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
204 assert_that(&Path::new(&path)).is_a_directory();
205 }
206
207 #[test]
208 pub fn should_not_panic_if_path_represents_a_file() {
209 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
210 assert_that(&Path::new(&path)).is_a_file();
211 }
212
213 #[test]
214 #[should_panic]
216 pub fn should_panic_if_path_does_not_represent_a_file() {
217 assert_that(&Path::new(&MANIFEST_PATH)).is_a_file();
218 }
219
220 #[test]
221 pub fn has_file_name_should_allow_multiple_borrow_forms_for_path() {
222 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
223 assert_that(&Path::new(&path)).has_file_name("Cargo.toml");
224 assert_that(&Path::new(&path)).has_file_name(&mut "Cargo.toml");
225 assert_that(&Path::new(&path)).has_file_name(&"Cargo.toml");
226 }
227
228 #[test]
229 pub fn should_not_panic_if_path_has_correct_file_name() {
230 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
231 assert_that(&Path::new(&path)).has_file_name(&"Cargo.toml");
232 }
233
234 #[test]
235 #[should_panic]
237 pub fn should_panic_if_path_does_not_have_correct_file_name() {
238 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
239 assert_that(&Path::new(&path)).has_file_name(&"pom.xml");
240 }
241
242 #[test]
243 #[should_panic]
245 pub fn should_panic_if_path_does_not_have_a_file_name() {
246 let path = MANIFEST_PATH.to_string() + "/..";
247 assert_that(&Path::new(&path)).has_file_name(&"pom.xml");
248 }
249
250 #[test]
251 pub fn should_not_panic_if_pathbuf_exists() {
252 assert_that(&PathBuf::from(MANIFEST_PATH)).exists();
253 }
254
255 #[test]
256 #[should_panic]
258 pub fn should_panic_if_pathbuf_does_not_exist() {
259 let failing_path = MANIFEST_PATH.to_string() + "/does-not-exist";
260 assert_that(&PathBuf::from(&failing_path)).exists();
261 }
262
263 #[test]
264 pub fn should_not_panic_if_pathbuf_represents_a_directory() {
265 assert_that(&PathBuf::from(MANIFEST_PATH)).is_a_directory();
266 }
267
268 #[test]
269 pub fn should_not_panic_if_pathbuf_does_not_exist_when_expected() {
270 let failing_path = MANIFEST_PATH.to_string() + "/does-not-exist";
271 assert_that(&PathBuf::from(&failing_path)).does_not_exist();
272 }
273
274 #[test]
275 #[should_panic]
277 pub fn should_panic_if_pathbuf_exists_when_not_expected() {
278 assert_that(&PathBuf::from(MANIFEST_PATH)).does_not_exist();
279 }
280
281 #[test]
282 #[should_panic]
284 pub fn should_panic_if_pathbuf_does_not_represent_a_directory() {
285 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
286 assert_that(&PathBuf::from(&path)).is_a_directory();
287 }
288
289 #[test]
290 pub fn should_not_panic_if_pathbuf_represents_a_file() {
291 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
292 assert_that(&PathBuf::from(&path)).is_a_file();
293 }
294
295 #[test]
296 #[should_panic]
298 pub fn should_panic_if_pathbuf_does_not_represent_a_file() {
299 assert_that(&PathBuf::from(&MANIFEST_PATH)).is_a_file();
300 }
301
302 #[test]
303 pub fn has_file_name_should_allow_multiple_borrow_forms_for_pathbuf() {
304 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
305 assert_that(&PathBuf::from(&path)).has_file_name("Cargo.toml");
306 assert_that(&PathBuf::from(&path)).has_file_name(&mut "Cargo.toml");
307 assert_that(&PathBuf::from(&path)).has_file_name(&"Cargo.toml");
308 }
309
310 #[test]
311 pub fn should_not_panic_if_pathbuf_has_correct_file_name() {
312 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
313 assert_that(&PathBuf::from(&path)).has_file_name(&"Cargo.toml");
314 }
315
316 #[test]
317 #[should_panic]
319 pub fn should_panic_if_pathbuf_does_not_have_correct_file_name() {
320 let path = MANIFEST_PATH.to_string() + "/Cargo.toml";
321 assert_that(&PathBuf::from(&path)).has_file_name(&"pom.xml");
322 }
323
324 #[test]
325 #[should_panic]
327 pub fn should_panic_if_pathbuf_does_not_have_a_file_name() {
328 let path = MANIFEST_PATH.to_string() + "/..";
329 assert_that(&PathBuf::from(&path)).has_file_name(&"pom.xml");
330 }
331}