use backslash::escape_ascii; use regex::Regex; use crate::choice::{Choice, ChoiceKind}; use crate::error::ParseRangeError; use crate::parse_error::ParseError; lazy_static! { static ref PARSE_CHOICE_RE: Regex = Regex::new(r"^(-?\d*)(:|\.\.=?)(-?\d*)$").unwrap(); } pub fn choice(src: &str) -> Result { let cap = match PARSE_CHOICE_RE.captures_iter(src).next() { Some(v) => v, None => match src.parse() { Ok(x) => return Ok(Choice::new(x, x, ChoiceKind::Single)), Err(e) => { eprintln!("failed to parse choice argument: {}", src); return Err(ParseError::ParseIntError(e)); } }, }; let start = if cap[1].is_empty() { 0 } else { match cap[1].parse() { Ok(x) => x, Err(e) => { eprintln!("failed to parse range start: {}", &cap[1]); return Err(ParseError::ParseIntError(e)); } } }; let kind = match &cap[2] { ":" => ChoiceKind::ColonRange, ".." => ChoiceKind::RustExclusiveRange, "..=" => ChoiceKind::RustInclusiveRange, _ => { eprintln!( "failed to parse range: not a valid range separator: {}", &cap[2] ); return Err(ParseError::ParseRangeError(ParseRangeError::new(&cap[2]))); } }; let end = if cap[3].is_empty() { isize::max_value() } else { match cap[3].parse() { Ok(x) => x, Err(e) => { eprintln!("failed to parse range end: {}", &cap[3]); return Err(ParseError::ParseIntError(e)); } } }; Ok(Choice::new(start, end, kind)) } pub fn output_field_separator(src: &str) -> String { escape_ascii(src).unwrap() } #[cfg(test)] mod tests { use crate::parse; mod parse_choice_tests { use super::*; #[test] fn parse_single_choice_start() { let result = parse::choice("6").unwrap(); assert_eq!(6, result.start) } #[test] fn parse_single_choice_end() { let result = parse::choice("6").unwrap(); assert_eq!(6, result.end) } #[test] fn parse_none_started_range() { let result = parse::choice(":5").unwrap(); assert_eq!((0, 5), (result.start, result.end)) } #[test] fn parse_none_terminated_range() { let result = parse::choice("5:").unwrap(); assert_eq!((5, isize::max_value()), (result.start, result.end)) } #[test] fn parse_full_range_pos_pos() { let result = parse::choice("5:7").unwrap(); assert_eq!((5, 7), (result.start, result.end)) } #[test] fn parse_full_range_neg_neg() { let result = parse::choice("-3:-1").unwrap(); assert_eq!((-3, -1), (result.start, result.end)) } #[test] fn parse_neg_started_none_ended() { let result = parse::choice("-3:").unwrap(); assert_eq!((-3, isize::max_value()), (result.start, result.end)) } #[test] fn parse_none_started_neg_ended() { let result = parse::choice(":-1").unwrap(); assert_eq!((0, -1), (result.start, result.end)) } #[test] fn parse_full_range_pos_neg() { let result = parse::choice("5:-3").unwrap(); assert_eq!((5, -3), (result.start, result.end)) } #[test] fn parse_full_range_neg_pos() { let result = parse::choice("-3:5").unwrap(); assert_eq!((-3, 5), (result.start, result.end)) } #[test] fn parse_beginning_to_end_range() { let result = parse::choice(":").unwrap(); assert_eq!((0, isize::max_value()), (result.start, result.end)) } #[test] fn parse_bad_choice() { assert!(parse::choice("d").is_err()); } #[test] fn parse_bad_range() { assert!(parse::choice("d:i").is_err()); } #[test] fn parse_rust_inclusive_range() { let result = parse::choice("3..=5").unwrap(); assert_eq!((3, 5), (result.start, result.end)) } #[test] fn parse_rust_inclusive_range_no_start() { let result = parse::choice("..=5").unwrap(); assert_eq!((0, 5), (result.start, result.end)) } #[test] fn parse_rust_inclusive_range_no_end() { let result = parse::choice("3..=").unwrap(); assert_eq!((3, isize::max_value()), (result.start, result.end)) } #[test] fn parse_rust_inclusive_range_no_start_or_end() { let result = parse::choice("..=").unwrap(); assert_eq!((0, isize::max_value()), (result.start, result.end)) } #[test] fn parse_full_range_pos_pos_rust_exclusive() { let result = parse::choice("5..7").unwrap(); assert_eq!((5, 7), (result.start, result.end)) } #[test] fn parse_full_range_neg_neg_rust_exclusive() { let result = parse::choice("-3..-1").unwrap(); assert_eq!((-3, -1), (result.start, result.end)) } #[test] fn parse_neg_started_none_ended_rust_exclusive() { let result = parse::choice("-3..").unwrap(); assert_eq!((-3, isize::max_value()), (result.start, result.end)) } #[test] fn parse_none_started_neg_ended_rust_exclusive() { let result = parse::choice("..-1").unwrap(); assert_eq!((0, -1), (result.start, result.end)) } #[test] fn parse_full_range_pos_neg_rust_exclusive() { let result = parse::choice("5..-3").unwrap(); assert_eq!((5, -3), (result.start, result.end)) } #[test] fn parse_full_range_neg_pos_rust_exclusive() { let result = parse::choice("-3..5").unwrap(); assert_eq!((-3, 5), (result.start, result.end)) } #[test] fn parse_rust_exclusive_range() { let result = parse::choice("3..5").unwrap(); assert_eq!((3, 5), (result.start, result.end)) } #[test] fn parse_rust_exclusive_range_no_start() { let result = parse::choice("..5").unwrap(); assert_eq!((0, 5), (result.start, result.end)) } #[test] fn parse_rust_exclusive_range_no_end() { let result = parse::choice("3..").unwrap(); assert_eq!((3, isize::max_value()), (result.start, result.end)) } #[test] fn parse_rust_exclusive_range_no_start_or_end() { let result = parse::choice("..").unwrap(); assert_eq!((0, isize::max_value()), (result.start, result.end)) } #[test] fn parse_full_range_pos_pos_rust_inclusive() { let result = parse::choice("5..=7").unwrap(); assert_eq!((5, 7), (result.start, result.end)) } #[test] fn parse_full_range_neg_neg_rust_inclusive() { let result = parse::choice("-3..=-1").unwrap(); assert_eq!((-3, -1), (result.start, result.end)) } #[test] fn parse_neg_started_none_ended_rust_inclusive() { let result = parse::choice("-3..=").unwrap(); assert_eq!((-3, isize::max_value()), (result.start, result.end)) } #[test] fn parse_none_started_neg_ended_rust_inclusive() { let result = parse::choice("..=-1").unwrap(); assert_eq!((0, -1), (result.start, result.end)) } #[test] fn parse_full_range_pos_neg_rust_inclusive() { let result = parse::choice("5..=-3").unwrap(); assert_eq!((5, -3), (result.start, result.end)) } #[test] fn parse_full_range_neg_pos_rust_inclusive() { let result = parse::choice("-3..=5").unwrap(); assert_eq!((-3, 5), (result.start, result.end)) } } }