use std::collections::BTreeSet;
use crate::browser::Kind;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Place {
Computer,
Keyboard,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum State {
Waiting,
Differs,
}
impl State {
pub fn title(self) -> &'static str {
match self {
State::Waiting => "Waiting to send",
State::Differs => "Differs",
}
}
pub fn word(self) -> &'static str {
match self {
State::Waiting => "waiting",
State::Differs => "differ",
}
}
pub fn sentence(self) -> &'static str {
match self {
State::Waiting => "everything owed back to the instrument",
State::Differs => "here and on the instrument, and the two bodies differ",
}
}
}
#[derive(Default)]
pub struct Filter {
pub kind: Option<Kind>,
pub tags: BTreeSet<u64>,
pub place: Option<Place>,
pub state: Option<State>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Narrow {
Kind(Kind),
Tag(u64),
Place(Place),
State(State),
}
impl Filter {
pub fn admits(
&self,
kind: Kind,
place: Place,
tags: &BTreeSet<u64>,
state: Option<State>,
) -> bool {
self.kind.is_none_or(|want| want == kind)
&& self.place.is_none_or(|want| want == place)
&& self.state.is_none_or(|want| Some(want) == state)
&& self.tags.is_subset(tags)
}
pub fn narrow(&mut self, narrow: Narrow) {
match narrow {
Narrow::Kind(kind) => self.kind = (self.kind != Some(kind)).then_some(kind),
Narrow::Tag(tag) => {
if !self.tags.remove(&tag) {
self.tags.insert(tag);
}
}
Narrow::Place(place) => self.place = (self.place != Some(place)).then_some(place),
Narrow::State(state) => self.state = (self.state != Some(state)).then_some(state),
}
}
pub fn on(&self, narrow: Narrow) -> bool {
match narrow {
Narrow::Kind(kind) => self.kind == Some(kind),
Narrow::Tag(tag) => self.tags.contains(&tag),
Narrow::Place(place) => self.place == Some(place),
Narrow::State(state) => self.state == Some(state),
}
}
pub fn forget_tag(&mut self, tag: u64) {
self.tags.remove(&tag);
}
pub fn keep_kinds(&mut self, present: &[Kind]) {
self.kind = self.kind.filter(|kind| present.contains(kind));
}
}
#[cfg(test)]
mod tests {
use super::*;
fn worn(tags: &[u64]) -> BTreeSet<u64> {
tags.iter().copied().collect()
}
#[test]
fn an_empty_filter_admits_every_row() {
let mut filter = Filter::default();
assert!(filter.admits(Kind::Program, Place::Computer, &worn(&[]), None));
assert!(filter.admits(Kind::Piano, Place::Keyboard, &worn(&[]), None));
filter.narrow(Narrow::Kind(Kind::Program));
assert!(filter.admits(Kind::Program, Place::Keyboard, &worn(&[]), None));
assert!(!filter.admits(Kind::Piano, Place::Keyboard, &worn(&[]), None));
filter.narrow(Narrow::Place(Place::Computer));
assert!(filter.admits(Kind::Program, Place::Computer, &worn(&[]), None));
assert!(!filter.admits(Kind::Program, Place::Keyboard, &worn(&[]), None));
}
#[test]
fn tags_narrow_together_rather_than_apart() {
let mut filter = Filter::default();
filter.narrow(Narrow::Tag(1));
assert!(filter.admits(Kind::Program, Place::Computer, &worn(&[1]), None));
assert!(filter.admits(Kind::Program, Place::Computer, &worn(&[1, 2]), None));
assert!(!filter.admits(Kind::Program, Place::Computer, &worn(&[2]), None));
filter.narrow(Narrow::Tag(2));
assert!(filter.admits(Kind::Program, Place::Computer, &worn(&[1, 2]), None));
assert!(!filter.admits(Kind::Program, Place::Computer, &worn(&[1]), None));
filter.narrow(Narrow::Kind(Kind::Program));
assert!(filter.admits(Kind::Program, Place::Computer, &worn(&[1, 2]), None));
assert!(!filter.admits(Kind::Live, Place::Computer, &worn(&[1, 2]), None));
}
#[test]
fn a_row_asked_for_twice_stops_narrowing() {
let mut filter = Filter::default();
for narrow in [
Narrow::Kind(Kind::Program),
Narrow::Tag(1),
Narrow::Place(Place::Computer),
Narrow::State(State::Waiting),
] {
filter.narrow(narrow);
assert!(filter.on(narrow), "{narrow:?}");
filter.narrow(narrow);
assert!(!filter.on(narrow), "{narrow:?}");
}
}
#[test]
fn a_state_admits_only_the_rows_in_it() {
let mut filter = Filter::default();
let admits = |filter: &Filter, state| {
filter.admits(Kind::Program, Place::Computer, &worn(&[]), state)
};
for state in [None, Some(State::Waiting), Some(State::Differs)] {
assert!(admits(&filter, state), "{state:?}");
}
filter.narrow(Narrow::State(State::Waiting));
assert!(admits(&filter, Some(State::Waiting)));
assert!(!admits(&filter, Some(State::Differs)));
assert!(!admits(&filter, None));
filter.narrow(Narrow::State(State::Differs));
assert!(admits(&filter, Some(State::Differs)));
assert!(!admits(&filter, Some(State::Waiting)));
filter.narrow(Narrow::Kind(Kind::Live));
assert!(!admits(&filter, Some(State::Differs)));
}
#[test]
fn a_tag_that_is_gone_stops_narrowing() {
let mut filter = Filter::default();
filter.narrow(Narrow::Tag(1));
filter.narrow(Narrow::Tag(2));
filter.forget_tag(1);
assert!(!filter.on(Narrow::Tag(1)));
assert!(filter.on(Narrow::Tag(2)));
}
}