1use std::fmt;
25use std::path;
26
27#[cfg(feature = "color")]
28use anstream::panic;
29use predicates::path::PredicateFileContentExt;
30use predicates::str::PredicateStrExt;
31use predicates_tree::CaseTreeExt;
32
33use crate::fixture;
34
35pub trait PathAssert {
64 #[track_caller]
100 fn assert<I, P>(&self, pred: I) -> &Self
101 where
102 I: IntoPathPredicate<P>,
103 P: predicates_core::Predicate<path::Path>;
104}
105
106impl PathAssert for fixture::TempDir {
107 #[track_caller]
108 fn assert<I, P>(&self, pred: I) -> &Self
109 where
110 I: IntoPathPredicate<P>,
111 P: predicates_core::Predicate<path::Path>,
112 {
113 assert(self.path(), pred);
114 self
115 }
116}
117
118impl PathAssert for fixture::NamedTempFile {
119 #[track_caller]
120 fn assert<I, P>(&self, pred: I) -> &Self
121 where
122 I: IntoPathPredicate<P>,
123 P: predicates_core::Predicate<path::Path>,
124 {
125 assert(self.path(), pred);
126 self
127 }
128}
129
130impl PathAssert for fixture::ChildPath {
131 #[track_caller]
132 fn assert<I, P>(&self, pred: I) -> &Self
133 where
134 I: IntoPathPredicate<P>,
135 P: predicates_core::Predicate<path::Path>,
136 {
137 assert(self.path(), pred);
138 self
139 }
140}
141
142#[track_caller]
143fn assert<I, P>(path: &path::Path, pred: I)
144where
145 I: IntoPathPredicate<P>,
146 P: predicates_core::Predicate<path::Path>,
147{
148 let pred = pred.into_path();
149 if let Some(case) = pred.find_case(false, path) {
150 let palette = crate::Palette::color();
151 panic!(
152 "Unexpected file, failed {:#}\n{:#}={:#}",
153 case.tree(),
154 palette.key("path"),
155 palette.value(path.display())
156 );
157 }
158}
159
160pub trait IntoPathPredicate<P>
179where
180 P: predicates_core::Predicate<path::Path>,
181{
182 type Predicate;
184
185 fn into_path(self) -> P;
187}
188
189impl<P> IntoPathPredicate<P> for P
190where
191 P: predicates_core::Predicate<path::Path>,
192{
193 type Predicate = P;
194
195 fn into_path(self) -> Self::Predicate {
196 self
197 }
198}
199
200#[derive(Debug)]
219pub struct BytesContentPathPredicate(
220 predicates::path::FileContentPredicate<predicates::ord::EqPredicate<&'static [u8]>>,
221);
222
223impl BytesContentPathPredicate {
224 pub(crate) fn new(value: &'static [u8]) -> Self {
225 let pred = predicates::ord::eq(value).from_file_path();
226 BytesContentPathPredicate(pred)
227 }
228}
229
230impl predicates_core::reflection::PredicateReflection for BytesContentPathPredicate {
231 fn parameters<'a>(
232 &'a self,
233 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
234 self.0.parameters()
235 }
236
237 fn children<'a>(
239 &'a self,
240 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
241 self.0.children()
242 }
243}
244
245impl predicates_core::Predicate<path::Path> for BytesContentPathPredicate {
246 fn eval(&self, item: &path::Path) -> bool {
247 self.0.eval(item)
248 }
249
250 fn find_case<'a>(
251 &'a self,
252 expected: bool,
253 variable: &path::Path,
254 ) -> Option<predicates_core::reflection::Case<'a>> {
255 self.0.find_case(expected, variable)
256 }
257}
258
259impl fmt::Display for BytesContentPathPredicate {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 self.0.fmt(f)
262 }
263}
264
265impl IntoPathPredicate<BytesContentPathPredicate> for &'static [u8] {
266 type Predicate = BytesContentPathPredicate;
267
268 fn into_path(self) -> Self::Predicate {
269 Self::Predicate::new(self)
270 }
271}
272
273#[derive(Debug, Clone)]
292pub struct StrContentPathPredicate(
293 predicates::path::FileContentPredicate<
294 predicates::str::Utf8Predicate<predicates::str::DifferencePredicate>,
295 >,
296);
297
298impl StrContentPathPredicate {
299 pub(crate) fn new(value: String) -> Self {
300 let pred = predicates::str::diff(value).from_utf8().from_file_path();
301 StrContentPathPredicate(pred)
302 }
303}
304
305impl predicates_core::reflection::PredicateReflection for StrContentPathPredicate {
306 fn parameters<'a>(
307 &'a self,
308 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
309 self.0.parameters()
310 }
311
312 fn children<'a>(
314 &'a self,
315 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
316 self.0.children()
317 }
318}
319
320impl predicates_core::Predicate<path::Path> for StrContentPathPredicate {
321 fn eval(&self, item: &path::Path) -> bool {
322 self.0.eval(item)
323 }
324
325 fn find_case<'a>(
326 &'a self,
327 expected: bool,
328 variable: &path::Path,
329 ) -> Option<predicates_core::reflection::Case<'a>> {
330 self.0.find_case(expected, variable)
331 }
332}
333
334impl fmt::Display for StrContentPathPredicate {
335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336 self.0.fmt(f)
337 }
338}
339
340impl IntoPathPredicate<StrContentPathPredicate> for String {
341 type Predicate = StrContentPathPredicate;
342
343 fn into_path(self) -> Self::Predicate {
344 Self::Predicate::new(self)
345 }
346}
347
348impl IntoPathPredicate<StrContentPathPredicate> for &str {
349 type Predicate = StrContentPathPredicate;
350
351 fn into_path(self) -> Self::Predicate {
352 Self::Predicate::new(self.to_owned())
353 }
354}
355
356impl IntoPathPredicate<StrContentPathPredicate> for &String {
357 type Predicate = StrContentPathPredicate;
358
359 fn into_path(self) -> Self::Predicate {
360 Self::Predicate::new(self.to_owned())
361 }
362}
363
364#[derive(Debug, Clone)]
384pub struct StrPathPredicate<P: predicates_core::Predicate<str>>(
385 predicates::path::FileContentPredicate<predicates::str::Utf8Predicate<P>>,
386);
387
388impl<P> StrPathPredicate<P>
389where
390 P: predicates_core::Predicate<str>,
391{
392 pub(crate) fn new(value: P) -> Self {
393 let pred = value.from_utf8().from_file_path();
394 StrPathPredicate(pred)
395 }
396}
397
398impl<P> predicates_core::reflection::PredicateReflection for StrPathPredicate<P>
399where
400 P: predicates_core::Predicate<str>,
401{
402 fn parameters<'a>(
403 &'a self,
404 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
405 self.0.parameters()
406 }
407
408 fn children<'a>(
410 &'a self,
411 ) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
412 self.0.children()
413 }
414}
415
416impl<P> predicates_core::Predicate<path::Path> for StrPathPredicate<P>
417where
418 P: predicates_core::Predicate<str>,
419{
420 fn eval(&self, item: &path::Path) -> bool {
421 self.0.eval(item)
422 }
423
424 fn find_case<'a>(
425 &'a self,
426 expected: bool,
427 variable: &path::Path,
428 ) -> Option<predicates_core::reflection::Case<'a>> {
429 self.0.find_case(expected, variable)
430 }
431}
432
433impl<P> fmt::Display for StrPathPredicate<P>
434where
435 P: predicates_core::Predicate<str>,
436{
437 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438 self.0.fmt(f)
439 }
440}
441
442impl<P> IntoPathPredicate<StrPathPredicate<P>> for P
443where
444 P: predicates_core::Predicate<str>,
445{
446 type Predicate = StrPathPredicate<P>;
447
448 fn into_path(self) -> Self::Predicate {
449 Self::Predicate::new(self)
450 }
451}
452
453#[cfg(test)]
454mod test {
455 use super::*;
456
457 use predicates::prelude::*;
458
459 fn convert_path<I, P>(pred: I) -> P
462 where
463 I: IntoPathPredicate<P>,
464 P: Predicate<path::Path>,
465 {
466 pred.into_path()
467 }
468
469 #[test]
470 fn into_path_from_pred() {
471 let pred = convert_path(predicate::eq(path::Path::new("hello.md")));
472 let case = pred.find_case(false, path::Path::new("hello.md"));
473 println!("Failing case: {case:?}");
474 assert!(case.is_none());
475 }
476
477 #[test]
478 fn into_path_from_bytes() {
479 let pred = convert_path(b"hello\n" as &[u8]);
480 let case = pred.find_case(false, path::Path::new("tests/fixture/hello.txt"));
481 println!("Failing case: {case:?}");
482 assert!(case.is_none());
483 }
484
485 #[test]
486 fn into_path_from_str() {
487 let pred = convert_path("hello\n");
488 let case = pred.find_case(false, path::Path::new("tests/fixture/hello.txt"));
489 println!("Failing case: {case:?}");
490 assert!(case.is_none());
491 }
492}