Skip to content

Commit a69f72f

Browse files
committed
Implement assert a file matches a fixture on disk
1 parent a5b0add commit a69f72f

File tree

5 files changed

+155
-11
lines changed

5 files changed

+155
-11
lines changed

src/path/fc.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,24 @@ use std::fmt;
1010
use std::fs;
1111
use std::io::{self, Read};
1212
use std::path;
13+
use std::str;
1314

1415
use Predicate;
1516

17+
#[derive(Clone, Debug, PartialEq)]
18+
pub struct FileContent(Vec<u8>);
19+
20+
impl FileContent {
21+
pub fn new(path: &path::Path) -> io::Result<FileContent> {
22+
let mut buffer = Vec::new();
23+
fs::File::open(path)?.read_to_end(&mut buffer)?;
24+
Ok(FileContent(buffer))
25+
}
26+
pub fn utf8(&self) -> Result<String, str::Utf8Error> {
27+
str::from_utf8(&self.0).map(|s| s.to_string())
28+
}
29+
}
30+
1631
/// Predicate adaper that converts a `path` to file content predicate to byte predicate.
1732
///
1833
/// This is created by `pred.from_path()`.
@@ -29,11 +44,8 @@ where
2944
P: Predicate<[u8]>,
3045
{
3146
fn eval(&self, path: &path::Path) -> io::Result<bool> {
32-
let mut buffer = Vec::new();
33-
34-
// read the whole file
35-
fs::File::open(path)?.read_to_end(&mut buffer)?;
36-
Ok(self.p.eval(&buffer))
47+
let buffer = FileContent::new(path)?;
48+
Ok(self.p.eval(&buffer.0))
3749
}
3850
}
3951

src/path/fs.rs

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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::io;
11+
use std::path;
12+
13+
use path::fc::FileContent;
14+
use Predicate;
15+
16+
/// Predicate that compares file matches
17+
#[derive(Clone, Debug)]
18+
pub struct BinaryFilePredicate {
19+
path: path::PathBuf,
20+
file_content: FileContent,
21+
}
22+
23+
impl BinaryFilePredicate {
24+
fn eval(&self, path: &path::Path) -> io::Result<bool> {
25+
let content = FileContent::new(path)?;
26+
Ok(self.file_content == content)
27+
}
28+
}
29+
30+
impl Predicate<path::Path> for BinaryFilePredicate {
31+
fn eval(&self, path: &path::Path) -> bool {
32+
self.eval(path).unwrap_or(false)
33+
}
34+
}
35+
36+
impl fmt::Display for BinaryFilePredicate {
37+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38+
write!(f, "var is {}", self.path.display())
39+
}
40+
}
41+
42+
/// Creates a new `Predicate` that ensures complete equality
43+
///
44+
/// # Examples
45+
///
46+
/// ```
47+
/// use std::path::Path;
48+
/// use predicates::prelude::*;
49+
///
50+
/// let predicate_file = predicate::path::eq_file(Path::new("Cargo.toml"));
51+
/// assert_eq!(true, predicate_file.eval(Path::new("Cargo.toml")));
52+
/// assert_eq!(false, predicate_file.eval(Path::new("src")));
53+
/// assert_eq!(false, predicate_file.eval(Path::new("Cargo.lock")));
54+
/// ```
55+
pub fn eq_file(path: &path::Path) -> BinaryFilePredicate {
56+
let file_content = FileContent::new(path).unwrap();
57+
BinaryFilePredicate {
58+
path: path.to_path_buf(),
59+
file_content,
60+
}
61+
}
62+
63+
impl BinaryFilePredicate {
64+
/// Creates a new `Predicate` that ensures complete equality
65+
///
66+
/// # Examples
67+
///
68+
/// ```
69+
/// use std::path::Path;
70+
/// use predicates::prelude::*;
71+
///
72+
/// let predicate_file = predicate::path::eq_file(Path::new("Cargo.toml")).utf8();
73+
/// assert_eq!(true, predicate_file.eval(Path::new("Cargo.toml")));
74+
/// assert_eq!(false, predicate_file.eval(Path::new("Cargo.lock")));
75+
/// assert_eq!(false, predicate_file.eval(Path::new("src")));
76+
/// ```
77+
pub fn utf8(self) -> StrFilePredicate {
78+
StrFilePredicate {
79+
path: self.path,
80+
content: self.file_content.utf8().unwrap(),
81+
}
82+
}
83+
}
84+
85+
/// Predicate that compares string content of files
86+
#[derive(Debug)]
87+
pub struct StrFilePredicate {
88+
path: path::PathBuf,
89+
content: String,
90+
}
91+
92+
impl StrFilePredicate {
93+
fn eval(&self, path: &path::Path) -> Option<bool> {
94+
let content = FileContent::new(path).ok()?.utf8().ok()?;
95+
Some(self.content == content)
96+
}
97+
}
98+
99+
impl fmt::Display for StrFilePredicate {
100+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101+
write!(f, "var is {}", self.path.display())
102+
}
103+
}
104+
105+
impl Predicate<path::Path> for StrFilePredicate {
106+
fn eval(&self, path: &path::Path) -> bool {
107+
self.eval(path).unwrap_or(false)
108+
}
109+
}

src/path/ft.rs

+23-3
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,32 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9-
use std::path;
109
use std::fmt;
1110
use std::fs;
11+
use std::io;
12+
use std::path;
1213

1314
use Predicate;
1415

15-
#[derive(Clone, Copy, Debug)]
16+
#[derive(Clone, Copy, Debug, PartialEq)]
1617
enum FileType {
1718
File,
1819
Dir,
1920
Symlink,
2021
}
2122

2223
impl FileType {
24+
fn new(path: &path::Path) -> io::Result<FileType> {
25+
let file_type = path.metadata()?.file_type();
26+
if file_type.is_dir() {
27+
return Ok(FileType::Dir);
28+
}
29+
if path.is_file() {
30+
return Ok(FileType::File);
31+
}
32+
Ok(FileType::Symlink)
33+
}
34+
2335
fn eval(self, ft: &fs::FileType) -> bool {
2436
match self {
2537
FileType::File => ft.is_file(),
@@ -43,7 +55,7 @@ impl fmt::Display for FileType {
4355
/// Predicate that checks the `std::fs::FileType`.
4456
///
4557
/// This is created by the `predicate::path::is_file`, `predicate::path::is_dir`, and `predicate::path::is_symlink`.
46-
#[derive(Debug)]
58+
#[derive(Clone, Debug)]
4759
pub struct FileTypePredicate {
4860
ft: FileType,
4961
follow: bool,
@@ -59,6 +71,14 @@ impl FileTypePredicate {
5971
self.follow = yes;
6072
self
6173
}
74+
75+
/// Allow to create an `FileTypePredicate` from a `path`
76+
pub fn from_path(path: &path::Path) -> io::Result<FileTypePredicate> {
77+
Ok(FileTypePredicate {
78+
ft: FileType::new(path)?,
79+
follow: true,
80+
})
81+
}
6282
}
6383

6484
impl Predicate<path::Path> for FileTypePredicate {

src/path/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ mod ft;
1616
pub use self::ft::{is_dir, is_file, is_symlink, FileTypePredicate};
1717
mod fc;
1818
pub use self::fc::{FileContentPredicate, PredicateFileContentExt};
19+
mod fs;
20+
pub use self::fs::{eq_file, BinaryFilePredicate, StrFilePredicate};

src/prelude.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@
88

99
//! Module that contains the essentials for working with predicates.
1010
11-
pub use core::Predicate;
1211
pub use boolean::PredicateBooleanExt;
1312
pub use boxed::PredicateBoxExt;
13+
pub use core::Predicate;
14+
pub use name::PredicateNameExt;
1415
pub use path::PredicateFileContentExt;
1516
pub use str::PredicateStrExt;
16-
pub use name::PredicateNameExt;
1717

1818
/// Predicate factories
1919
pub mod predicate {
2020
// primitive `Predicate` types
2121
pub use constant::{always, never};
2222
pub use function::function;
23-
pub use ord::{eq, ge, gt, le, lt, ne};
2423
pub use iter::{in_hash, in_iter};
24+
pub use ord::{eq, ge, gt, le, lt, ne};
2525

2626
/// `str` Predicate factories
2727
///
@@ -41,6 +41,7 @@ pub mod predicate {
4141
///
4242
/// This module contains predicates specific to path handling.
4343
pub mod path {
44+
pub use path::eq_file;
4445
pub use path::{exists, missing};
4546
pub use path::{is_dir, is_file, is_symlink};
4647
}

0 commit comments

Comments
 (0)