use nom::error::ErrorKind;
use std::ops::RangeInclusive;
use std::str::FromStr;
mod parser;
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Error {
#[error("Invalid token {}", 0)]
ParsingFailed(ErrorKind),
}
#[derive(Debug, PartialEq)]
pub enum Selection {
All,
Some(Vec<SomeElementType>),
None,
}
impl Selection {
pub fn contains_item(&self, item: usize) -> bool {
match self {
Selection::Some(v) => {
v.iter()
.any(|element| {
match element {
SomeElementType::Individual(num) => item == *num,
SomeElementType::Range(range) => range.contains(&item),
}
})
},
Selection::All => true,
Selection::None => false,
}
}
}
#[derive(Debug, PartialEq)]
pub enum SomeElementType {
Individual(usize),
Range(RangeInclusive<usize>),
}
pub use parser::parse as parse_raw;
pub fn parse(input: &str) -> Result<Selection, Error> {
input.parse()
}
impl FromStr for Selection {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match parse_raw(s)? {
Selection::Some(v) => Selection::Some(condense_selections(v)),
other => other,
})
}
}
fn condense_selections(selections: Vec<SomeElementType>) -> Vec<SomeElementType> {
let mut union = range_union_find::IntRangeUnionFind::new();
selections
.iter()
.map(|a| match a {
SomeElementType::Individual(num) => *num..=*num,
SomeElementType::Range(range) => range.clone(),
})
.filter(|a| !a.is_empty())
.try_for_each(|a| union.insert_range(&a))
.expect("bad ranges - shouldn't happen is bug");
let v = union.into_collection::<Vec<_>>();
v.into_iter()
.map(|a| {
if a.start() == a.end() {
SomeElementType::Individual(*a.start())
} else {
SomeElementType::Range(a)
}
})
.collect()
}
#[cfg(test)]
mod helper_tests {
use super::*;
#[test]
fn selection_contains_item() {
assert!(Selection::All.contains_item(6543268));
assert!(!Selection::None.contains_item(385188));
assert!(Selection::Some(vec![SomeElementType::Individual(1)]).contains_item(1));
assert!(Selection::Some(vec![SomeElementType::Range(1..=1)]).contains_item(1));
assert!(Selection::Some(vec![SomeElementType::Range(1..=3)]).contains_item(1));
assert!(Selection::Some(vec![SomeElementType::Range(1..=3)]).contains_item(3));
let selection = Selection::Some(vec![
SomeElementType::Individual(2),
SomeElementType::Individual(6),
SomeElementType::Range(4..=8),
]);
assert!(selection.contains_item(2));
assert!(selection.contains_item(6));
assert!(selection.contains_item(5));
assert!(selection.contains_item(8));
assert!(selection.contains_item(4));
assert!(!selection.contains_item(3));
assert!(!selection.contains_item(9));
assert!(!selection.contains_item(1));
assert!(!selection.contains_item(3));
}
#[test]
fn condense_ranges() {
let c = condense_selections(vec![
SomeElementType::Individual(1),
SomeElementType::Individual(3),
SomeElementType::Range(5..=9),
SomeElementType::Individual(8),
SomeElementType::Individual(10),
]);
assert_eq!(
c,
vec![
SomeElementType::Individual(1),
SomeElementType::Individual(3),
SomeElementType::Range(5..=10),
]
);
}
#[test]
fn condense_ranges_more_complex() {
let c = condense_selections(vec![
SomeElementType::Individual(1),
SomeElementType::Individual(3),
SomeElementType::Range(5..=9),
SomeElementType::Range(11..=20),
SomeElementType::Individual(10),
]);
assert_eq!(
c,
vec![
SomeElementType::Individual(1),
SomeElementType::Individual(3),
SomeElementType::Range(5..=20),
]
);
}
}