diff options
author | Ryan Geary <rtgnj42@gmail.com> | 2019-09-15 18:16:42 -0400 |
---|---|---|
committer | Ryan Geary <rtgnj42@gmail.com> | 2019-09-17 23:40:30 -0400 |
commit | fd66e3cfb7f935b00befd7b04609cef7464e6e67 (patch) | |
tree | 0ca0e2840222eb61a2b033ba1c13c5c56359dec2 /src/main.rs | |
parent | 6c889b3963c797da78918f35709c3a12d0931cb0 (diff) |
Move most of the processing out of main
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 156 |
1 files changed, 2 insertions, 154 deletions
diff --git a/src/main.rs b/src/main.rs index 4d2e2bb..de1eb0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,163 +1,11 @@ -use regex::Regex; -use std::convert::TryInto; use std::fs::File; use std::io::{self, BufRead, BufReader, Read}; -use std::num::ParseIntError; -use std::path::PathBuf; -use std::process; use structopt::StructOpt; -type Range = (Option<u32>, Option<u32>); - -#[derive(Debug)] -enum Choice { - Field(u32), - FieldRange(Range), -} - -impl Choice { - fn print_choice(&self, line: &String, opt: &Opt) { - let re = Regex::new(match &opt.field_separator { - Some(s) => s, - None => "[[:space:]]", - }) - .unwrap_or_else(|e| { - eprintln!("Failed to compile regular expression: {}", e); - // Exit code of 1 means failed to compile field_separator regex - process::exit(1); - }); - - let words = re - .split(line) - .into_iter() - .filter(|s| !s.is_empty()) - .enumerate(); - - match self { - Choice::Field(i) => { - print!( - "{} ", - words - .filter(|x| x.0 == *i as usize) - .map(|x| x.1) - .collect::<String>() - ); - } - Choice::FieldRange(r) => match r { - (None, None) => print!("{}", words.map(|x| x.1).collect::<String>()), - (Some(start), None) => print!( - "{} ", - words - .filter(|x| x.0 >= (*start).try_into().unwrap()) - .map(|x| x.1) - .collect::<Vec<&str>>() - .join(" ") - ), - (None, Some(end)) => { - let e: usize = if opt.inclusive { - (end + 1).try_into().unwrap() - } else { - (*end).try_into().unwrap() - }; - print!( - "{} ", - words - .filter(|x| x.0 < e) - .map(|x| x.1) - .collect::<Vec<&str>>() - .join(" ") - ) - } - (Some(start), Some(end)) => { - let e: usize = if opt.inclusive { - (end + 1).try_into().unwrap() - } else { - (*end).try_into().unwrap() - }; - print!( - "{} ", - words - .filter(|x| x.0 < e && x.0 >= (*start).try_into().unwrap()) - .map(|x| x.1) - .collect::<Vec<&str>>() - .join(" ") - ) - } - }, - }; - } - - fn parse_choice(src: &str) -> Result<Choice, ParseIntError> { - let re = Regex::new(r"^(\d*):(\d*)$").unwrap(); - - let cap = match re.captures_iter(src).next() { - Some(v) => v, - None => match src.parse() { - Ok(x) => return Ok(Choice::Field(x)), - Err(_) => { - eprintln!("failed to parse choice argument: {}", src); - // Exit code of 2 means failed to parse choice argument - process::exit(2); - } - }, - }; - - let start = if cap[1].is_empty() { - None - } else { - match cap[1].parse() { - Ok(x) => Some(x), - Err(_) => { - eprintln!("failed to parse range start: {}", &cap[1]); - process::exit(2); - } - } - }; - - let end = if cap[2].is_empty() { - None - } else { - match cap[2].parse() { - Ok(x) => Some(x), - Err(_) => { - eprintln!("failed to parse range end: {}", &cap[2]); - process::exit(2); - } - } - }; - - return Ok(Choice::FieldRange((start, end))); - } -} - -#[derive(Debug, StructOpt)] -#[structopt(name = "choose", about = "`choose` sections from each line of files")] -struct Opt { - /// Specify field separator other than whitespace - #[structopt(short, long)] - field_separator: Option<String>, - - /// Use inclusive ranges - #[structopt(short = "n", long)] - inclusive: bool, - - /// Activate debug mode - #[structopt(short, long)] - debug: bool, - - /// Input file - #[structopt(short, long, parse(from_os_str))] - input: Option<PathBuf>, - - /// Fields to print. Either x, x:, :y, or x:y, where x and y are integers, colons indicate a - /// range, and an empty field on either side of the colon continues to the beginning or end of - /// the line. - #[structopt(required = true, min_values = 1, parse(try_from_str = Choice::parse_choice))] - choice: Vec<Choice>, -} +mod choice; fn main() { - let opt = Opt::from_args(); + let opt = choice::Opt::from_args(); let read = match &opt.input { Some(f) => Box::new(File::open(f).expect("Could not open file")) as Box<dyn Read>, |