use anyhow::{format_err, Error};
use serde::{Deserialize, Serialize};
use std::ops::RangeInclusive;
mod conversions;
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(try_from = "String")]
pub struct OffsetString(String);
impl OffsetString {
pub fn new<T: AsRef<str>>(source: T) -> Result<OffsetString, Error> {
let mut segment_index = 0;
for segment in source.as_ref().split(',') {
let mut endpoints = segment.split('+');
let low = endpoints.next().ok_or_else(|| format_err!("Empty segment"))?;
let low_int = low.parse::<u32>()?;
if segment_index > 0 && low_int <= 1 {
return Err(format_err!("Adjacent ranges must be merged"));
}
if let Some(span) = endpoints.next() {
let span_int = span.parse::<u32>()?;
if span_int < 1 {
return Err(format_err!("Range is too small: {}", &segment));
}
if endpoints.next().is_some() {
return Err(format_err!("Invalid segment: {}", &segment));
}
}
segment_index += 1;
}
Ok(OffsetString(source.as_ref().to_string()))
}
pub fn iter_ranges<'a>(&'a self) -> impl Iterator<Item = RangeInclusive<u32>> + 'a {
self.0
.split(',')
.map(|segment| {
segment.split('+').map(|s| s.parse::<u32>().unwrap()).collect::<Vec<u32>>()
})
.scan(0u32, |offset, parsed_ints| {
let low = *offset + parsed_ints[0];
let high = if parsed_ints.len() == 1 { low } else { low + parsed_ints[1] };
*offset = high;
Some(low..=high)
})
}
pub fn iter<'a>(&'a self) -> impl Iterator<Item = u32> + 'a {
self.iter_ranges().flat_map(|range| range)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_offset_string_new() {
assert!(OffsetString::new("0+4,5,11+8").is_ok());
assert!(OffsetString::new("1+3,5,11+8").is_ok());
}
#[test]
fn test_offset_string_new_bad_string() {
assert!(OffsetString::new("3+,5,11+8").is_err());
assert!(OffsetString::new("-5+4,5,11+8").is_err());
assert!(OffsetString::new("3+1,a,11+8").is_err());
assert!(OffsetString::new("3+1,5,11+8,").is_err());
}
#[test]
fn test_offset_string_new_bad_offset() {
assert!(OffsetString::new("0+4,0,5,11+8").is_err());
assert!(OffsetString::new("0+4,1,5,11+8").is_err());
assert!(OffsetString::new("0+4,1+3,5,11+8").is_err());
}
#[test]
fn test_offset_string_iter_ranges() -> Result<(), Error> {
let offset_string = OffsetString::new("0+4,5,11+8")?;
assert_eq!(
offset_string.iter_ranges().collect::<Vec<RangeInclusive<u32>>>(),
vec![0..=4, 9..=9, 20..=28]
);
Ok(())
}
#[test]
fn test_offset_string_iter() -> Result<(), Error> {
let offset_string = OffsetString::new("0+4,5,11+8")?;
assert_eq!(
offset_string.iter().collect::<Vec<u32>>(),
vec![0, 1, 2, 3, 4, 9, 20, 21, 22, 23, 24, 25, 26, 27, 28]
);
Ok(())
}
}