assert-rs / predicates-rs

Boolean-valued predicate functions in Rust
docs.rs/predicates
Apache License 2.0
173 stars 29 forks source link

Implement Predicate<PathBuf> for BinaryFilePredicate #74

Open asomers opened 5 years ago

asomers commented 5 years ago

It would be handy if BinaryFilePredicate implemented Predicate<PathBuf>. There are some functions that, for lifetime reasons, must take a 'static argument, like PathBuf. Right now those functions can't easily use predicates.

epage commented 5 years ago

I just gave it a try and didn't have any problems

#!/usr/bin/env run-cargo-script
//! ```cargo
//! [package]
//! edition="2018"
//! [dependencies]
//! predicates = "1"
//! ```
use predicates::prelude::*;
fn main() {
    let actual = std::path::PathBuf::from("Cargo.toml");
    let expected = std::path::PathBuf::from("Cargo.toml");
    let pred = predicate::path::eq_file(expected);
    println!("{}", pred.eval(actual.as_path()));
}

Could you clarify where you are having lifetime problems at?

asomers commented 5 years ago

It worked for you because of that as_path() on the last line. In my case I'm using predicates from a generic function, so I can't do that.

epage commented 5 years ago

Could you provide an example so I can better understand the constraints and we can work out how predicates might be able to change to make it work?

asomers commented 5 years ago

It's basically like this. The matches function doesn't know anything about what type it's handling. It's not very critical for me, though.

#!/usr/bin/env run-cargo-script
//! ```cargo
//! [package]
//! edition="2018"
//! [dependencies]
//! predicates = "1"
//! ```

fn matches<T, P: Predicate<T>>(value: &T, predicate: P) -> bool {
    predicate.eval(value)
}

use predicates::prelude::*;
fn main() {
    let actual = std::path::PathBuf::from("Cargo.toml");
    let expected = std::path::PathBuf::from("Cargo.toml");
    let pred = predicate::path::eq_file(expected);
    println!("{}", matches(&actual, pred));
}
epage commented 5 years ago

Thanks for the example. For me to better understand the needs / use cases, is there a reason matches(actual.as_path(), pred) isn't sufficient?

asomers commented 5 years ago

Well, there's more generic code in front of that matches, and that code takes its argument by value, like this:

#!/usr/bin/env run-cargo-script
//! ```cargo
//! [package]
//! edition="2018"
//! [dependencies]
//! predicates = "1"
//! ```

fn do_something_with_value<T>(_value: T) {
}

fn do_something_with_value_if_it_matches<T, P>(value: T, predicate: P)
    where P: Predicate<T>
{
    if matches(&value, predicate) {
        do_something_with_value(value)
    }
}

fn matches<T, P: Predicate<T>>(value: &T, predicate: P) -> bool {
    predicate.eval(value)
}

use predicates::prelude::*;
fn main() {
    let actual = std::path::PathBuf::from("Cargo.toml");
    let expected = std::path::PathBuf::from("Cargo.toml");
    let pred = predicate::path::eq_file(expected);
    do_something_with_value_if_it_matches(actual, pred);
}
epage commented 5 years ago

btw this is related to #20 where I ran into a lot of problems solving this generically.

Some options