blob: aceed55777408114861fbe6acbe6c7da0c355b13 [file] [log] [blame]
// Copyright (c) 2018 The predicates-rs Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fmt;
use std::fs;
use std::io;
use std::path;
use crate::reflection;
use crate::Predicate;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum FileType {
File,
Dir,
Symlink,
}
impl FileType {
fn from_path(path: &path::Path, follow: bool) -> io::Result<FileType> {
let file_type = if follow {
path.metadata()
} else {
path.symlink_metadata()
}?
.file_type();
if file_type.is_dir() {
return Ok(FileType::Dir);
}
if file_type.is_file() {
return Ok(FileType::File);
}
Ok(FileType::Symlink)
}
fn eval(self, ft: fs::FileType) -> bool {
match self {
FileType::File => ft.is_file(),
FileType::Dir => ft.is_dir(),
FileType::Symlink => ft.is_symlink(),
}
}
}
impl fmt::Display for FileType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let t = match *self {
FileType::File => "file",
FileType::Dir => "dir",
FileType::Symlink => "symlink",
};
write!(f, "{}", t)
}
}
/// 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, Clone, Copy, PartialEq, Eq)]
pub struct FileTypePredicate {
ft: FileType,
follow: bool,
}
impl FileTypePredicate {
/// Follow symbolic links.
///
/// When yes is true, symbolic links are followed as if they were normal directories and files.
///
/// Default: disabled.
pub fn follow_links(mut self, yes: bool) -> Self {
self.follow = yes;
self
}
/// Allow to create an `FileTypePredicate` from a `path`
pub fn from_path(path: &path::Path) -> io::Result<FileTypePredicate> {
Ok(FileTypePredicate {
ft: FileType::from_path(path, true)?,
follow: true,
})
}
}
impl Predicate<path::Path> for FileTypePredicate {
fn eval(&self, path: &path::Path) -> bool {
let metadata = if self.follow {
path.metadata()
} else {
path.symlink_metadata()
};
metadata
.map(|m| self.ft.eval(m.file_type()))
.unwrap_or(false)
}
fn find_case<'a>(
&'a self,
expected: bool,
variable: &path::Path,
) -> Option<reflection::Case<'a>> {
let actual_type = FileType::from_path(variable, self.follow);
match (expected, actual_type) {
(_, Ok(actual_type)) => {
let result = self.ft == actual_type;
if result == expected {
Some(
reflection::Case::new(Some(self), result)
.add_product(reflection::Product::new("actual filetype", actual_type)),
)
} else {
None
}
}
(true, Err(_)) => None,
(false, Err(err)) => Some(
reflection::Case::new(Some(self), false)
.add_product(reflection::Product::new("error", err)),
),
}
}
}
impl reflection::PredicateReflection for FileTypePredicate {
fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> {
let params = vec![reflection::Parameter::new("follow", &self.follow)];
Box::new(params.into_iter())
}
}
impl fmt::Display for FileTypePredicate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let palette = crate::Palette::new(f.alternate());
write!(
f,
"{} {} {}",
palette.var("var"),
palette.description("is"),
palette.expected(self.ft)
)
}
}
/// Creates a new `Predicate` that ensures the path points to a file.
///
/// # Examples
///
/// ```
/// use std::path::Path;
/// use predicates::prelude::*;
///
/// let predicate_fn = predicate::path::is_file();
/// assert_eq!(true, predicate_fn.eval(Path::new("Cargo.toml")));
/// assert_eq!(false, predicate_fn.eval(Path::new("src")));
/// assert_eq!(false, predicate_fn.eval(Path::new("non-existent-file.foo")));
/// ```
pub fn is_file() -> FileTypePredicate {
FileTypePredicate {
ft: FileType::File,
follow: false,
}
}
/// Creates a new `Predicate` that ensures the path points to a directory.
///
/// # Examples
///
/// ```
/// use std::path::Path;
/// use predicates::prelude::*;
///
/// let predicate_fn = predicate::path::is_dir();
/// assert_eq!(false, predicate_fn.eval(Path::new("Cargo.toml")));
/// assert_eq!(true, predicate_fn.eval(Path::new("src")));
/// assert_eq!(false, predicate_fn.eval(Path::new("non-existent-file.foo")));
/// ```
pub fn is_dir() -> FileTypePredicate {
FileTypePredicate {
ft: FileType::Dir,
follow: false,
}
}
/// Creates a new `Predicate` that ensures the path points to a symlink.
///
/// # Examples
///
/// ```
/// use std::path::Path;
/// use predicates::prelude::*;
///
/// let predicate_fn = predicate::path::is_symlink();
/// assert_eq!(false, predicate_fn.eval(Path::new("Cargo.toml")));
/// assert_eq!(false, predicate_fn.eval(Path::new("src")));
/// assert_eq!(false, predicate_fn.eval(Path::new("non-existent-file.foo")));
/// ```
pub fn is_symlink() -> FileTypePredicate {
FileTypePredicate {
ft: FileType::Symlink,
follow: false,
}
}