summaryrefslogtreecommitdiffstats
path: root/src/choice.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/choice.rs')
-rw-r--r--src/choice.rs379
1 files changed, 264 insertions, 115 deletions
diff --git a/src/choice.rs b/src/choice.rs
index 62477c1..b7bf879 100644
--- a/src/choice.rs
+++ b/src/choice.rs
@@ -1,89 +1,74 @@
-use crate::io::{BufWriter, Write};
-use std::convert::TryInto;
-
use crate::config::Config;
-
-pub type Range = (Option<u32>, Option<u32>);
+use crate::io::{BufWriter, Write};
#[derive(Debug)]
-pub enum Choice {
- Field(u32),
- FieldRange(Range),
+pub struct Choice {
+ pub start: usize,
+ pub end: usize,
}
impl Choice {
- pub fn print_choice(
+ pub fn new(start: usize, end: usize) -> Self {
+ Choice { start, end }
+ }
+
+ pub fn print_choice<WriterType: Write>(
&self,
line: &String,
config: &Config,
- handle: &mut BufWriter<std::io::StdoutLock>,
+ handle: &mut BufWriter<WriterType>,
) {
- write!(handle, "{}", self.get_choice_slice(line, config).join(" "));
- }
-
- pub fn is_reverse_range(&self) -> bool {
- match self {
- Choice::Field(_) => false,
- Choice::FieldRange(r) => match r {
- (Some(start), Some(end)) => end < start,
- _ => false,
- },
- }
- }
+ let mut line_iter = config.separator.split(line).filter(|s| !s.is_empty());
- fn get_choice_slice<'a>(&self, line: &'a String, config: &Config) -> Vec<&'a str> {
- let words = config
- .separator
- .split(line)
- .into_iter()
- .filter(|s| !s.is_empty())
- .enumerate();
-
- let mut slices = match self {
- Choice::Field(i) => words
- .filter(|x| x.0 == *i as usize)
- .map(|x| x.1)
- .collect::<Vec<&str>>(),
- Choice::FieldRange(r) => match r {
- (None, None) => words.map(|x| x.1).collect::<Vec<&str>>(),
- (Some(start), None) => words
- .filter(|x| x.0 >= (*start).try_into().unwrap())
- .map(|x| x.1)
- .collect::<Vec<&str>>(),
- (None, Some(end)) => {
- let e: usize = if config.opt.exclusive {
- (end - 1).try_into().unwrap()
- } else {
- (*end).try_into().unwrap()
- };
- words
- .filter(|x| x.0 <= e)
- .map(|x| x.1)
- .collect::<Vec<&str>>()
+ if self.is_reverse_range() {
+ if self.end > 0 {
+ line_iter.nth(self.end - 1);
+ }
+
+ let mut stack = Vec::new();
+ for i in 0..=(self.start - self.end) {
+ match line_iter.next() {
+ Some(s) => stack.push(s),
+ None => break,
}
- (Some(start), Some(end)) => {
- let e: usize = if config.opt.exclusive {
- (end - 1).try_into().unwrap()
- } else {
- (*end).try_into().unwrap()
- };
- words
- .filter(|x| {
- (x.0 <= e && x.0 >= (*start).try_into().unwrap())
- || self.is_reverse_range()
- && (x.0 >= e && x.0 <= (*start).try_into().unwrap())
- })
- .map(|x| x.1)
- .collect::<Vec<&str>>()
+
+ if self.start <= self.end + i {
+ break;
}
- },
- };
+ }
- if self.is_reverse_range() {
- slices.reverse();
+ loop {
+ match stack.pop() {
+ Some(s) => Choice::write_bytes(handle, s.as_bytes()),
+ None => break,
+ }
+ }
+ } else {
+ if self.start > 0 {
+ line_iter.nth(self.start - 1);
+ }
+
+ for i in 0..=(self.end - self.start) {
+ match line_iter.next() {
+ Some(s) => Choice::write_bytes(handle, s.as_bytes()),
+ None => break,
+ };
+
+ if self.end <= self.start + i {
+ break;
+ }
+ }
}
+ }
- return slices;
+ fn write_bytes<WriterType: Write>(handle: &mut BufWriter<WriterType>, b: &[u8]) {
+ handle.write(b).unwrap();
+ handle.write(b" ").unwrap();
+ }
+
+ #[cfg_attr(feature = "flame_it", flame)]
+ pub fn is_reverse_range(&self) -> bool {
+ self.end < self.start
}
}
@@ -92,6 +77,7 @@ mod tests {
use crate::config::{Config, Opt};
use std::ffi::OsString;
+ use std::io::{self, BufWriter, Write};
use structopt::StructOpt;
impl Config {
@@ -104,128 +90,293 @@ mod tests {
}
}
- mod get_choice_slice_tests {
+ struct MockStdout {
+ pub buffer: String,
+ }
+
+ impl MockStdout {
+ fn new() -> Self {
+ MockStdout {
+ buffer: String::new(),
+ }
+ }
+
+ fn str_from_buf_writer(b: BufWriter<MockStdout>) -> String {
+ match b.into_inner() {
+ Ok(b) => b.buffer,
+ Err(_) => panic!("Failed to access BufWriter inner writer"),
+ }
+ .trim_end()
+ .to_string()
+ }
+ }
+
+ impl Write for MockStdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let mut bytes_written = 0;
+ for i in buf {
+ self.buffer.push(*i as char);
+ bytes_written += 1;
+ }
+ Ok(bytes_written)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+ }
+
+ mod print_choice_tests {
use super::*;
#[test]
fn print_0() {
let config = Config::from_iter(vec!["choose", "0"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+
+ config.opt.choice[0].print_choice(
+ &String::from("rust is pretty cool"),
+ &config,
+ &mut handle,
+ );
+
assert_eq!(
- vec!["rust"],
- config.opt.choice[0]
- .get_choice_slice(&String::from("rust is pretty cool"), &config)
+ String::from("rust"),
+ MockStdout::str_from_buf_writer(handle)
);
}
#[test]
fn print_after_end() {
let config = Config::from_iter(vec!["choose", "10"]);
- assert_eq!(
- Vec::<&str>::new(),
- config.opt.choice[0]
- .get_choice_slice(&String::from("rust is pretty cool"), &config)
+ let mut handle = BufWriter::new(MockStdout::new());
+
+ config.opt.choice[0].print_choice(
+ &String::from("rust is pretty cool"),
+ &config,
+ &mut handle,
);
+
+ assert_eq!(String::new(), MockStdout::str_from_buf_writer(handle));
}
#[test]
fn print_out_of_order() {
let config = Config::from_iter(vec!["choose", "3", "1"]);
- assert_eq!(
- vec!["cool"],
- config.opt.choice[0]
- .get_choice_slice(&String::from("rust is pretty cool"), &config)
+ let mut handle = BufWriter::new(MockStdout::new());
+ let mut handle1 = BufWriter::new(MockStdout::new());
+
+ config.opt.choice[0].print_choice(
+ &String::from("rust is pretty cool"),
+ &config,
+ &mut handle,
);
+
assert_eq!(
- vec!["is"],
- config.opt.choice[1]
- .get_choice_slice(&String::from("rust is pretty cool"), &config)
+ String::from("cool"),
+ MockStdout::str_from_buf_writer(handle)
);
+
+ config.opt.choice[1].print_choice(
+ &String::from("rust is pretty cool"),
+ &config,
+ &mut handle1,
+ );
+
+ assert_eq!(String::from("is"), MockStdout::str_from_buf_writer(handle1));
}
#[test]
fn print_1_to_3_exclusive() {
let config = Config::from_iter(vec!["choose", "1:3", "-x"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust is pretty cool"),
+ &config,
+ &mut handle,
+ );
assert_eq!(
- vec!["is", "pretty"],
- config.opt.choice[0]
- .get_choice_slice(&String::from("rust is pretty cool"), &config)
+ String::from("is pretty"),
+ MockStdout::str_from_buf_writer(handle)
);
}
#[test]
fn print_1_to_3() {
let config = Config::from_iter(vec!["choose", "1:3"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust is pretty cool"),
+ &config,
+ &mut handle,
+ );
assert_eq!(
- vec!["is", "pretty", "cool"],
- config.opt.choice[0]
- .get_choice_slice(&String::from("rust is pretty cool"), &config)
+ String::from("is pretty cool"),
+ MockStdout::str_from_buf_writer(handle)
);
}
#[test]
fn print_1_to_3_separated_by_hashtag() {
let config = Config::from_iter(vec!["choose", "1:3", "-f", "#"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust#is#pretty#cool"),
+ &config,
+ &mut handle,
+ );
assert_eq!(
- vec!["is", "pretty", "cool"],
- config.opt.choice[0]
- .get_choice_slice(&String::from("rust#is#pretty#cool"), &config)
+ String::from("is pretty cool"),
+ MockStdout::str_from_buf_writer(handle)
);
}
#[test]
fn print_1_to_3_separated_by_varying_multiple_hashtag_exclusive() {
let config = Config::from_iter(vec!["choose", "1:3", "-f", "#", "-x"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust##is###pretty####cool"),
+ &config,
+ &mut handle,
+ );
assert_eq!(
- vec!["is", "pretty"],
- config.opt.choice[0]
- .get_choice_slice(&String::from("rust##is###pretty####cool"), &config)
+ String::from("is pretty"),
+ MockStdout::str_from_buf_writer(handle)
);
}
#[test]
fn print_1_to_3_separated_by_varying_multiple_hashtag() {
let config = Config::from_iter(vec!["choose", "1:3", "-f", "#"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust##is###pretty####cool"),
+ &config,
+ &mut handle,
+ );
assert_eq!(
- vec!["is", "pretty", "cool"],
- config.opt.choice[0]
- .get_choice_slice(&String::from("rust##is###pretty####cool"), &config)
+ String::from("is pretty cool"),
+ MockStdout::str_from_buf_writer(handle)
);
}
#[test]
fn print_1_to_3_separated_by_regex_group_vowels_exclusive() {
let config = Config::from_iter(vec!["choose", "1:3", "-f", "[aeiou]", "-x"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("the quick brown fox jumped over the lazy dog"),
+ &config,
+ &mut handle,
+ );
assert_eq!(
- vec![" q", "ck br"],
- config.opt.choice[0].get_choice_slice(
- &String::from("the quick brown fox jumped over the lazy dog"),
- &config
- )
+ String::from(" q ck br"),
+ MockStdout::str_from_buf_writer(handle)
);
}
#[test]
fn print_1_to_3_separated_by_regex_group_vowels() {
let config = Config::from_iter(vec!["choose", "1:3", "-f", "[aeiou]"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("the quick brown fox jumped over the lazy dog"),
+ &config,
+ &mut handle,
+ );
assert_eq!(
- vec![" q", "ck br", "wn f"],
- config.opt.choice[0].get_choice_slice(
- &String::from("the quick brown fox jumped over the lazy dog"),
- &config
- )
+ String::from(" q ck br wn f"),
+ MockStdout::str_from_buf_writer(handle)
);
}
#[test]
fn print_3_to_1() {
let config = Config::from_iter(vec!["choose", "3:1"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust lang is pretty darn cool"),
+ &config,
+ &mut handle,
+ );
assert_eq!(
- vec!["pretty", "is", "lang"],
- config.opt.choice[0]
- .get_choice_slice(&String::from("rust lang is pretty darn cool"), &config)
+ String::from("pretty is lang"),
+ MockStdout::str_from_buf_writer(handle)
+ );
+ }
+
+ #[test]
+ fn print_3_to_1_exclusive() {
+ let config = Config::from_iter(vec!["choose", "3:1", "-x"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust lang is pretty darn cool"),
+ &config,
+ &mut handle,
+ );
+ assert_eq!(
+ String::from("is lang"),
+ MockStdout::str_from_buf_writer(handle)
+ );
+ }
+
+ #[test]
+ fn print_1_to_3_nonexistant_field_separator() {
+ let config = Config::from_iter(vec!["choose", "1:3", "-f", "#"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust lang is pretty darn cool"),
+ &config,
+ &mut handle,
);
+ assert_eq!(String::from(""), MockStdout::str_from_buf_writer(handle));
}
+ #[test]
+ fn print_0_nonexistant_field_separator() {
+ let config = Config::from_iter(vec!["choose", "0", "-f", "#"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust lang is pretty darn cool"),
+ &config,
+ &mut handle,
+ );
+ assert_eq!(
+ String::from("rust lang is pretty darn cool"),
+ MockStdout::str_from_buf_writer(handle)
+ );
+ }
+
+ #[test]
+ fn print_0_to_3_nonexistant_field_separator() {
+ let config = Config::from_iter(vec!["choose", "0:3", "-f", "#"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from("rust lang is pretty darn cool"),
+ &config,
+ &mut handle,
+ );
+ assert_eq!(
+ String::from("rust lang is pretty darn cool"),
+ MockStdout::str_from_buf_writer(handle)
+ );
+ }
+
+ #[test]
+ fn print_0_with_preceding_separator() {
+ let config = Config::from_iter(vec!["choose", "0"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(
+ &String::from(" rust lang is pretty darn cool"),
+ &config,
+ &mut handle,
+ );
+ assert_eq!(
+ String::from("rust"),
+ MockStdout::str_from_buf_writer(handle)
+ );
+ }
}
mod is_reverse_range_tests {
@@ -260,7 +411,5 @@ mod tests {
let config = Config::from_iter(vec!["choose", "4:2"]);
assert_eq!(true, config.opt.choice[0].is_reverse_range());
}
-
}
-
}