diff --git a/src/path/fc.rs b/src/path/fc.rs index 8509092..b1d856a 100644 --- a/src/path/fc.rs +++ b/src/path/fc.rs @@ -10,9 +10,24 @@ use std::fmt; use std::fs; use std::io::{self, Read}; use std::path; +use std::str; use Predicate; +#[derive(Clone, Debug, PartialEq)] +pub struct FileContent(Vec); + +impl FileContent { + pub fn new(path: &path::Path) -> io::Result { + let mut buffer = Vec::new(); + fs::File::open(path)?.read_to_end(&mut buffer)?; + Ok(FileContent(buffer)) + } + pub fn utf8(&self) -> Result { + str::from_utf8(&self.0).map(|s| s.to_string()) + } +} + /// Predicate adaper that converts a `path` to file content predicate to byte predicate. /// /// This is created by `pred.from_path()`. @@ -29,11 +44,8 @@ where P: Predicate<[u8]>, { fn eval(&self, path: &path::Path) -> io::Result { - let mut buffer = Vec::new(); - - // read the whole file - fs::File::open(path)?.read_to_end(&mut buffer)?; - Ok(self.p.eval(&buffer)) + let buffer = FileContent::new(path)?; + Ok(self.p.eval(&buffer.0)) } } diff --git a/src/path/fs.rs b/src/path/fs.rs new file mode 100644 index 0000000..5da5953 --- /dev/null +++ b/src/path/fs.rs @@ -0,0 +1,109 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; +use std::io; +use std::path; + +use path::fc::FileContent; +use Predicate; + +/// Predicate that compares file matches +#[derive(Clone, Debug)] +pub struct BinaryFilePredicate { + path: path::PathBuf, + file_content: FileContent, +} + +impl BinaryFilePredicate { + fn eval(&self, path: &path::Path) -> io::Result { + let content = FileContent::new(path)?; + Ok(self.file_content == content) + } +} + +impl Predicate for BinaryFilePredicate { + fn eval(&self, path: &path::Path) -> bool { + self.eval(path).unwrap_or(false) + } +} + +impl fmt::Display for BinaryFilePredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var is {}", self.path.display()) + } +} + +/// Creates a new `Predicate` that ensures complete equality +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use predicates::prelude::*; +/// +/// let predicate_file = predicate::path::eq_file(Path::new("Cargo.toml")); +/// assert_eq!(true, predicate_file.eval(Path::new("Cargo.toml"))); +/// assert_eq!(false, predicate_file.eval(Path::new("src"))); +/// assert_eq!(false, predicate_file.eval(Path::new("Cargo.lock"))); +/// ``` +pub fn eq_file(path: &path::Path) -> BinaryFilePredicate { + let file_content = FileContent::new(path).unwrap(); + BinaryFilePredicate { + path: path.to_path_buf(), + file_content, + } +} + +impl BinaryFilePredicate { + /// Creates a new `Predicate` that ensures complete equality + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// use predicates::prelude::*; + /// + /// let predicate_file = predicate::path::eq_file(Path::new("Cargo.toml")).utf8(); + /// assert_eq!(true, predicate_file.eval(Path::new("Cargo.toml"))); + /// assert_eq!(false, predicate_file.eval(Path::new("Cargo.lock"))); + /// assert_eq!(false, predicate_file.eval(Path::new("src"))); + /// ``` + pub fn utf8(self) -> StrFilePredicate { + StrFilePredicate { + path: self.path, + content: self.file_content.utf8().unwrap(), + } + } +} + +/// Predicate that compares string content of files +#[derive(Debug)] +pub struct StrFilePredicate { + path: path::PathBuf, + content: String, +} + +impl StrFilePredicate { + fn eval(&self, path: &path::Path) -> Option { + let content = FileContent::new(path).ok()?.utf8().ok()?; + Some(self.content == content) + } +} + +impl fmt::Display for StrFilePredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var is {}", self.path.display()) + } +} + +impl Predicate for StrFilePredicate { + fn eval(&self, path: &path::Path) -> bool { + self.eval(path).unwrap_or(false) + } +} diff --git a/src/path/ft.rs b/src/path/ft.rs index 9122248..0541813 100644 --- a/src/path/ft.rs +++ b/src/path/ft.rs @@ -6,13 +6,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::path; use std::fmt; use std::fs; +use std::io; +use std::path; use Predicate; -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] enum FileType { File, Dir, @@ -20,6 +21,17 @@ enum FileType { } impl FileType { + fn new(path: &path::Path) -> io::Result { + let file_type = path.metadata()?.file_type(); + if file_type.is_dir() { + return Ok(FileType::Dir); + } + if path.is_file() { + return Ok(FileType::File); + } + Ok(FileType::Symlink) + } + fn eval(self, ft: &fs::FileType) -> bool { match self { FileType::File => ft.is_file(), @@ -43,7 +55,7 @@ impl fmt::Display for FileType { /// Predicate that checks the `std::fs::FileType`. /// /// This is created by the `predicate::path::is_file`, `predicate::path::is_dir`, and `predicate::path::is_symlink`. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct FileTypePredicate { ft: FileType, follow: bool, @@ -59,6 +71,14 @@ impl FileTypePredicate { self.follow = yes; self } + + /// Allow to create an `FileTypePredicate` from a `path` + pub fn from_path(path: &path::Path) -> io::Result { + Ok(FileTypePredicate { + ft: FileType::new(path)?, + follow: true, + }) + } } impl Predicate for FileTypePredicate { diff --git a/src/path/mod.rs b/src/path/mod.rs index 9954c54..8c0ed9a 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -16,3 +16,5 @@ mod ft; pub use self::ft::{is_dir, is_file, is_symlink, FileTypePredicate}; mod fc; pub use self::fc::{FileContentPredicate, PredicateFileContentExt}; +mod fs; +pub use self::fs::{eq_file, BinaryFilePredicate, StrFilePredicate}; diff --git a/src/prelude.rs b/src/prelude.rs index 02fd93d..70c754c 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -8,20 +8,20 @@ //! Module that contains the essentials for working with predicates. -pub use core::Predicate; pub use boolean::PredicateBooleanExt; pub use boxed::PredicateBoxExt; +pub use core::Predicate; +pub use name::PredicateNameExt; pub use path::PredicateFileContentExt; pub use str::PredicateStrExt; -pub use name::PredicateNameExt; /// Predicate factories pub mod predicate { // primitive `Predicate` types pub use constant::{always, never}; pub use function::function; - pub use ord::{eq, ge, gt, le, lt, ne}; pub use iter::{in_hash, in_iter}; + pub use ord::{eq, ge, gt, le, lt, ne}; /// `str` Predicate factories /// @@ -41,6 +41,7 @@ pub mod predicate { /// /// This module contains predicates specific to path handling. pub mod path { + pub use path::eq_file; pub use path::{exists, missing}; pub use path::{is_dir, is_file, is_symlink}; }