predicates-rs
predicates-rs copied to clipboard
Add `map` combinator to map input values to a predicate
I am working on a todo app and I use predicates-rs to filter tasks before displaying them to the user. My Task struct contains a id: TaskId field and I want to use predicate::in_hash to filter out tasks that have an id that is within a certain list.
I currently have code that looks roughly like this:
// base predicate that lets everything through
let predicate = predicate::always().boxed(); // BoxPredicate<Task>
if filter_unactionable {
let tasks_with_uncompleted_dependencies: Vec<TaskId> = task_database.iter_tasks().filter(|| ...).map(|t| t.id).collect();
let has_uncompleted_dependencies = predicate::in_hash(tasks_with_uncompleted_dependencies); // HashableInPredicate<TaskId>
predicate = predicate.and(has_uncompleted_dependencies.not()).boxed(); // error, differing types!
}
// other filters here
// `predicate` is returned and later used to filter which tasks to display
This code does not work because my in_hash predicate expects a value of type TaskId while my main predicate expects Task.
To solve this, I'd like a way to "map" values before a predicate evaluates them. This could look like this (API is just a suggestion):
let predicate = predicate::always().boxed(); // BoxPredicate<Task>
if filter_unactionable {
let tasks_with_uncompleted_dependencies: Vec<TaskId> = task_database.iter_tasks().filter(|| ...).map(|t| t.id).collect();
let has_uncompleted_dependencies = predicate::in_hash(tasks_with_uncompleted_dependencies); // HashableInPredicate<TaskId>
// map from Predicate<Task> to PredicateTaskId
let has_uncompleted_dependencies_mapped = has_uncompleted_dependencies.map(|task: &Task| task.task_id);
predicate = predicate.and(has_uncompleted_dependencies_mapped.not()).boxed();
}
Would you be up for prototyping this within your project, creating extension traits to provide the syntax you want?
This will
- Unblock you
- Allow experimentation and iteration before being pulled into predicates
- Help clarify what your request is by having it in a real-worl application
I've already created my own predicate in my own project here: https://github.com/holly-hacker/td/commit/4f71543393cb662b01759128028bf3e76447f044#diff-ea57b46e48fccc26585162b2fa81cc63b45aa766280a22afa43c881a356b23cdR197
Usage looks roughly like this
let tasks_with_uncompleted_dependencies = self.database.get_all_tasks()
.filter(|t| ...)
.map(|t| t.id().clone())
.collect::<HashSet<_>>();
let has_uncompleted_dependencies = predicate::in_hash(tasks_with_uncompleted_dependencies);
let has_uncompleted_dependencies = MapPredicate::new(has_uncompleted_dependencies, |task: &Task| task.id());
predicate = predicate.and(has_uncompleted_dependencies.not()).boxed();
I opted to not write an extension trait for my current solution, but an ideal api would look something like this instead:
let has_uncompleted_dependencies = has_uncompleted_dependencies.map(|task: &Task| task.id()));