use std::num;
use std::str::FromStr;
pub type RangeElement = usize;
#[derive(Debug, PartialEq)]
pub enum CreateRangeError {
NonPositiveStep(RangeElement),
InvertedBounds(RangeElement, RangeElement),
}
#[derive(Debug, PartialEq)]
pub struct Range {
start: RangeElement,
end: RangeElement,
step: RangeElement, }
impl Range {
pub fn new(
start: RangeElement,
end: RangeElement,
step: RangeElement,
) -> Result<Self, CreateRangeError> {
if step < 1 {
Err(CreateRangeError::NonPositiveStep(step))
}
else if start > end {
Err(CreateRangeError::InvertedBounds(start, end))
}
else {
if start == end {
Ok(Self { start, end, step: 1 })
}
else {
Ok(Self { start, end, step })
}
}
}
pub fn contains(&self, i: RangeElement) -> bool {
i >= self.start && i <= self.end && (i - self.start).is_multiple_of(self.step)
}
}
#[derive(Debug, PartialEq)]
pub enum ParseRangeError {
EmptyString,
RangeSyntaxError,
ParseIntError(num::ParseIntError),
CreateRangeError(CreateRangeError),
}
impl From<num::ParseIntError> for ParseRangeError {
fn from(error: num::ParseIntError) -> Self {
ParseRangeError::ParseIntError(error)
}
}
impl From<CreateRangeError> for ParseRangeError {
fn from(error: CreateRangeError) -> Self {
ParseRangeError::CreateRangeError(error)
}
}
fn parse_or_default_if_empty(
s: &str,
default_if_empty: RangeElement,
) -> Result<RangeElement, num::ParseIntError> {
if s.is_empty() {
Ok(default_if_empty)
}
else {
s.parse::<RangeElement>()
}
}
impl FromStr for Range {
type Err = ParseRangeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err(ParseRangeError::EmptyString);
}
let bounds_and_step: (&str, RangeElement) = match s.split_once('/') {
Some((bs, ss)) => (bs, ss.parse::<RangeElement>()?),
None => (s, 1),
};
let bounds_str = bounds_and_step.0;
let (start_str, end_str) = match bounds_str.split_once([':', '-']) {
Some(t) => t,
None => (bounds_str, bounds_str),
};
let start = parse_or_default_if_empty(start_str, RangeElement::MIN)?;
let end = parse_or_default_if_empty(end_str, RangeElement::MAX)?;
Ok(Range::new(start, end, bounds_and_step.1)?)
}
}