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::Predicate; |
16 | |
17 | fn read_file(path: &path::Path) -> io::Result<Vec<u8>> { |
18 | let mut buffer = Vec::new(); |
19 | fs::File::open(path)?.read_to_end(&mut buffer)?; |
20 | Ok(buffer) |
21 | } |
22 | |
23 | /// Predicate adapter that converts a `path` predicate to a byte predicate on its content. |
24 | /// |
25 | /// This is created by `pred.from_path()`. |
26 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
27 | pub struct FileContentPredicate<P> |
28 | where |
29 | P: Predicate<[u8]>, |
30 | { |
31 | p: P, |
32 | } |
33 | |
34 | impl<P> FileContentPredicate<P> |
35 | where |
36 | P: Predicate<[u8]>, |
37 | { |
38 | fn eval(&self, path: &path::Path) -> io::Result<bool> { |
39 | let buffer = read_file(path)?; |
40 | Ok(self.p.eval(&buffer)) |
41 | } |
42 | } |
43 | |
44 | impl<P> reflection::PredicateReflection for FileContentPredicate<P> |
45 | where |
46 | P: Predicate<[u8]>, |
47 | { |
48 | fn children<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Child<'a>> + 'a> { |
49 | let params = vec![reflection::Child::new("predicate" , &self.p)]; |
50 | Box::new(params.into_iter()) |
51 | } |
52 | } |
53 | |
54 | impl<P> fmt::Display for FileContentPredicate<P> |
55 | where |
56 | P: Predicate<[u8]>, |
57 | { |
58 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
59 | self.p.fmt(f) |
60 | } |
61 | } |
62 | |
63 | impl<P> Predicate<path::Path> for FileContentPredicate<P> |
64 | where |
65 | P: Predicate<[u8]>, |
66 | { |
67 | fn eval(&self, path: &path::Path) -> bool { |
68 | self.eval(path).unwrap_or(false) |
69 | } |
70 | |
71 | fn find_case<'a>( |
72 | &'a self, |
73 | expected: bool, |
74 | variable: &path::Path, |
75 | ) -> Option<reflection::Case<'a>> { |
76 | let buffer = read_file(variable); |
77 | match (expected, buffer) { |
78 | (_, Ok(buffer)) => self.p.find_case(expected, &buffer).map(|case| { |
79 | case.add_product(reflection::Product::new( |
80 | "var" , |
81 | variable.display().to_string(), |
82 | )) |
83 | }), |
84 | (true, Err(_)) => None, |
85 | (false, Err(err)) => Some( |
86 | reflection::Case::new(Some(self), false) |
87 | .add_product(reflection::Product::new( |
88 | "var" , |
89 | variable.display().to_string(), |
90 | )) |
91 | .add_product(reflection::Product::new("error" , err)), |
92 | ), |
93 | } |
94 | } |
95 | } |
96 | |
97 | /// `Predicate` extension adapting a `slice` Predicate. |
98 | pub trait PredicateFileContentExt |
99 | where |
100 | Self: Predicate<[u8]>, |
101 | Self: Sized, |
102 | { |
103 | /// Returns a `FileContentPredicate` that adapts `Self` to a file content `Predicate`. |
104 | /// |
105 | /// # Examples |
106 | /// |
107 | /// ``` |
108 | /// use predicates::prelude::*; |
109 | /// use std::path::Path; |
110 | /// |
111 | /// let predicate_fn = predicate::str::is_empty().not().from_utf8().from_file_path(); |
112 | /// assert_eq!(true, predicate_fn.eval(Path::new("./tests/hello_world" ))); |
113 | /// assert_eq!(false, predicate_fn.eval(Path::new("./tests/empty_file" ))); |
114 | /// ``` |
115 | #[allow (clippy::wrong_self_convention)] |
116 | fn from_file_path(self) -> FileContentPredicate<Self> { |
117 | FileContentPredicate { p: self } |
118 | } |
119 | } |
120 | |
121 | impl<P> PredicateFileContentExt for P where P: Predicate<[u8]> {} |
122 | |