use std::fmt;
use std::path;
#[cfg(feature = "color")]
use anstream::panic;
use predicates::path::PredicateFileContentExt;
use predicates::str::PredicateStrExt;
use predicates_tree::CaseTreeExt;
use crate::fixture;
pub trait PathAssert {
#[track_caller]
fn assert<I, P>(&self, pred: I) -> &Self
where
I: IntoPathPredicate<P>,
P: predicates_core::Predicate<path::Path>;
}
impl PathAssert for fixture::TempDir {
#[track_caller]
fn assert<I, P>(&self, pred: I) -> &Self
where
I: IntoPathPredicate<P>,
P: predicates_core::Predicate<path::Path>,
{
assert(self.path(), pred);
self
}
}
impl PathAssert for fixture::NamedTempFile {
#[track_caller]
fn assert<I, P>(&self, pred: I) -> &Self
where
I: IntoPathPredicate<P>,
P: predicates_core::Predicate<path::Path>,
{
assert(self.path(), pred);
self
}
}
impl PathAssert for fixture::ChildPath {
#[track_caller]
fn assert<I, P>(&self, pred: I) -> &Self
where
I: IntoPathPredicate<P>,
P: predicates_core::Predicate<path::Path>,
{
assert(self.path(), pred);
self
}
}
#[track_caller]
fn assert<I, P>(path: &path::Path, pred: I)
where
I: IntoPathPredicate<P>,
P: predicates_core::Predicate<path::Path>,
{
let pred = pred.into_path();
if let Some(case) = pred.find_case(false, path) {
let palette = crate::Palette::color();
panic!(
"Unexpected file, failed {:#}\n{:#}={:#}",
case.tree(),
palette.key("path"),
palette.value(path.display())
);
}
}
pub trait IntoPathPredicate<P>
where
P: predicates_core::Predicate<path::Path>,
{
type Predicate;
fn into_path(self) -> P;
}
impl<P> IntoPathPredicate<P> for P
where
P: predicates_core::Predicate<path::Path>,
{
type Predicate = P;
fn into_path(self) -> Self::Predicate {
self
}
}
#[derive(Debug)]
pub struct BytesContentPathPredicate(
predicates::path::FileContentPredicate<predicates::ord::EqPredicate<&'static [u8]>>,
);
impl BytesContentPathPredicate {
pub(crate) fn new(value: &'static [u8]) -> Self {
let pred = predicates::ord::eq(value).from_file_path();
BytesContentPathPredicate(pred)
}
}
impl predicates_core::reflection::PredicateReflection for BytesContentPathPredicate {
fn parameters<'a>(
&'a self,
) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
self.0.parameters()
}
fn children<'a>(
&'a self,
) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
self.0.children()
}
}
impl predicates_core::Predicate<path::Path> for BytesContentPathPredicate {
fn eval(&self, item: &path::Path) -> bool {
self.0.eval(item)
}
fn find_case<'a>(
&'a self,
expected: bool,
variable: &path::Path,
) -> Option<predicates_core::reflection::Case<'a>> {
self.0.find_case(expected, variable)
}
}
impl fmt::Display for BytesContentPathPredicate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl IntoPathPredicate<BytesContentPathPredicate> for &'static [u8] {
type Predicate = BytesContentPathPredicate;
fn into_path(self) -> Self::Predicate {
Self::Predicate::new(self)
}
}
#[derive(Debug, Clone)]
pub struct StrContentPathPredicate(
predicates::path::FileContentPredicate<
predicates::str::Utf8Predicate<predicates::str::DifferencePredicate>,
>,
);
impl StrContentPathPredicate {
pub(crate) fn new(value: String) -> Self {
let pred = predicates::str::diff(value).from_utf8().from_file_path();
StrContentPathPredicate(pred)
}
}
impl predicates_core::reflection::PredicateReflection for StrContentPathPredicate {
fn parameters<'a>(
&'a self,
) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
self.0.parameters()
}
fn children<'a>(
&'a self,
) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
self.0.children()
}
}
impl predicates_core::Predicate<path::Path> for StrContentPathPredicate {
fn eval(&self, item: &path::Path) -> bool {
self.0.eval(item)
}
fn find_case<'a>(
&'a self,
expected: bool,
variable: &path::Path,
) -> Option<predicates_core::reflection::Case<'a>> {
self.0.find_case(expected, variable)
}
}
impl fmt::Display for StrContentPathPredicate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl IntoPathPredicate<StrContentPathPredicate> for String {
type Predicate = StrContentPathPredicate;
fn into_path(self) -> Self::Predicate {
Self::Predicate::new(self)
}
}
impl IntoPathPredicate<StrContentPathPredicate> for &str {
type Predicate = StrContentPathPredicate;
fn into_path(self) -> Self::Predicate {
Self::Predicate::new(self.to_owned())
}
}
impl IntoPathPredicate<StrContentPathPredicate> for &String {
type Predicate = StrContentPathPredicate;
fn into_path(self) -> Self::Predicate {
Self::Predicate::new(self.to_owned())
}
}
#[derive(Debug, Clone)]
pub struct StrPathPredicate<P: predicates_core::Predicate<str>>(
predicates::path::FileContentPredicate<predicates::str::Utf8Predicate<P>>,
);
impl<P> StrPathPredicate<P>
where
P: predicates_core::Predicate<str>,
{
pub(crate) fn new(value: P) -> Self {
let pred = value.from_utf8().from_file_path();
StrPathPredicate(pred)
}
}
impl<P> predicates_core::reflection::PredicateReflection for StrPathPredicate<P>
where
P: predicates_core::Predicate<str>,
{
fn parameters<'a>(
&'a self,
) -> Box<dyn Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
self.0.parameters()
}
fn children<'a>(
&'a self,
) -> Box<dyn Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
self.0.children()
}
}
impl<P> predicates_core::Predicate<path::Path> for StrPathPredicate<P>
where
P: predicates_core::Predicate<str>,
{
fn eval(&self, item: &path::Path) -> bool {
self.0.eval(item)
}
fn find_case<'a>(
&'a self,
expected: bool,
variable: &path::Path,
) -> Option<predicates_core::reflection::Case<'a>> {
self.0.find_case(expected, variable)
}
}
impl<P> fmt::Display for StrPathPredicate<P>
where
P: predicates_core::Predicate<str>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<P> IntoPathPredicate<StrPathPredicate<P>> for P
where
P: predicates_core::Predicate<str>,
{
type Predicate = StrPathPredicate<P>;
fn into_path(self) -> Self::Predicate {
Self::Predicate::new(self)
}
}
#[cfg(test)]
mod test {
use super::*;
use predicates::prelude::*;
fn convert_path<I, P>(pred: I) -> P
where
I: IntoPathPredicate<P>,
P: Predicate<path::Path>,
{
pred.into_path()
}
#[test]
fn into_path_from_pred() {
let pred = convert_path(predicate::eq(path::Path::new("hello.md")));
let case = pred.find_case(false, path::Path::new("hello.md"));
println!("Failing case: {case:?}");
assert!(case.is_none());
}
#[test]
fn into_path_from_bytes() {
let pred = convert_path(b"hello\n" as &[u8]);
let case = pred.find_case(false, path::Path::new("tests/fixture/hello.txt"));
println!("Failing case: {case:?}");
assert!(case.is_none());
}
#[test]
fn into_path_from_str() {
let pred = convert_path("hello\n");
let case = pred.find_case(false, path::Path::new("tests/fixture/hello.txt"));
println!("Failing case: {case:?}");
assert!(case.is_none());
}
}