mod range;
use std::{io, iter};
pub use range::{CreateRangeError, ParseRangeError, Range, RangeElement};
#[derive(Debug, PartialEq)]
pub enum ParseArgumentsError {
Help,
CreateRangeError(range::CreateRangeError),
ParseRangeError(range::ParseRangeError),
}
impl From<range::ParseRangeError> for ParseArgumentsError {
fn from(error: range::ParseRangeError) -> Self {
ParseArgumentsError::ParseRangeError(error)
}
}
impl From<range::CreateRangeError> for ParseArgumentsError {
fn from(error: range::CreateRangeError) -> Self {
ParseArgumentsError::CreateRangeError(error)
}
}
pub fn parse_arguments<T: AsRef<str>>(args: &[T]) -> Result<Vec<Range>, ParseArgumentsError> {
if args.iter().any(|s| s.as_ref() == "-h" || s.as_ref() == "--help") {
Err(ParseArgumentsError::Help)
}
else {
args.iter()
.flat_map(|arg| arg.as_ref().split(","))
.map(|r| r.parse::<Range>().map_err(ParseArgumentsError::ParseRangeError))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::{ParseArgumentsError, Range, parse_arguments};
#[test]
fn no_arguments() {
let result = parse_arguments(&Vec::<&str>::new()).unwrap();
assert_eq!(result, Vec::<Range>::new());
}
#[test]
fn single_number() {
let result = parse_arguments(&["1"]).unwrap();
let expected = vec![Range::new(1, 1, 1).unwrap()];
assert_eq!(result, expected);
}
#[test]
fn single_range() {
let result = parse_arguments(&["1-10"]).unwrap();
assert_eq!(result, vec![Range::new(1, 10, 1).unwrap()]);
}
#[test]
fn single_range_with_step() {
let result = parse_arguments(&["1-10/2"]).unwrap();
assert_eq!(result, vec![Range::new(1, 10, 2).unwrap()]);
}
#[test]
fn single_arg_multiple_ranges_with_steps() {
let result = parse_arguments(&["1-10/2,12-25/3"]).unwrap();
assert_eq!(result, vec![Range::new(1, 10, 2).unwrap(), Range::new(12, 25, 3).unwrap()]);
}
#[test]
fn multiple_args() {
let result = parse_arguments(&["1-10/2", "12-25/3"]).unwrap();
assert_eq!(result, vec![Range::new(1, 10, 2).unwrap(), Range::new(12, 25, 3).unwrap()]);
}
#[test]
fn multiple_args_multiple_ranges() {
let result = parse_arguments(&["1-10/2,12-25/3", "30-40/5"]).unwrap();
assert_eq!(
result,
vec![
Range::new(1, 10, 2).unwrap(),
Range::new(12, 25, 3).unwrap(),
Range::new(30, 40, 5).unwrap()
]
);
}
#[test]
fn short_help() {
let result = parse_arguments(&["-h"]);
assert_eq!(result.unwrap_err(), ParseArgumentsError::Help);
}
#[test]
fn long_help() {
let result = parse_arguments(&["--help"]);
assert_eq!(result.unwrap_err(), ParseArgumentsError::Help);
}
#[test]
fn long_help_with_other_args() {
let result = parse_arguments(&["1-10/2", "--help"]);
assert_eq!(result.unwrap_err(), ParseArgumentsError::Help);
}
}
pub fn print_usage(program: &str) {
eprintln!("Usage: {program} <range>[,<range>]...");
eprintln!("Read standard input and print the lines selected by the given ranges.");
eprintln!();
eprintln!(" --help display this help and exit");
}
pub fn select_elements<T>(
source: impl iter::Iterator<Item = T>,
ranges: &[Range],
) -> impl iter::Iterator<Item = T> {
source.enumerate().filter_map(|(i, elem)| {
if ranges.iter().any(|r| r.contains(i + 1)) {
Some(elem)
}
else {
None
}
})
}
pub fn print_selected_lines(ranges: &[Range]) {
select_elements(io::stdin().lines(), ranges).for_each(|line| println!("{}", line.unwrap()))
}