summaryrefslogtreecommitdiffstats
path: root/src/choice.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/choice.rs')
-rw-r--r--src/choice.rs282
1 files changed, 185 insertions, 97 deletions
diff --git a/src/choice.rs b/src/choice.rs
index 2f89ff0..bf193f6 100644
--- a/src/choice.rs
+++ b/src/choice.rs
@@ -1,7 +1,10 @@
use std::convert::TryInto;
+use std::io::{BufWriter, Write};
+use std::iter::FromIterator;
use crate::config::Config;
-use crate::io::{BufWriter, Write};
+use crate::writeable::Writeable;
+use crate::writer::WriteReceiver;
#[derive(Debug)]
pub struct Choice {
@@ -29,134 +32,149 @@ impl Choice {
config: &Config,
handle: &mut BufWriter<W>,
) {
- let mut line_iter = config
- .separator
- .split(line)
- .filter(|s| !s.is_empty() || config.opt.non_greedy);
-
- if self.is_reverse_range() && !self.has_negative_index() {
- if self.end > 0 {
- line_iter.nth((self.end - 1).try_into().unwrap());
- }
+ if config.opt.character_wise {
+ let line_chars = line[0..line.len() - 1].chars();
+ self.print_choice_generic(line_chars, config, handle);
+ } else {
+ let line_iter = config
+ .separator
+ .split(line)
+ .filter(|s| !s.is_empty() || config.opt.non_greedy);
+ self.print_choice_generic(line_iter, config, handle);
+ }
+ }
- let mut stack = Vec::new();
- for i in 0..=(self.start - self.end) {
- match line_iter.next() {
- Some(s) => stack.push(s),
- None => break,
- }
+ pub fn is_reverse_range(&self) -> bool {
+ self.reversed
+ }
- if self.start <= self.end + i {
- break;
- }
- }
+ pub fn has_negative_index(&self) -> bool {
+ self.negative_index
+ }
- let mut iter = stack.iter().rev().peekable();
- loop {
- match iter.next() {
- Some(s) => {
- Choice::write_bytes(s.as_bytes(), config, handle, iter.peek().is_some())
- }
- None => break,
- }
- }
+ fn print_choice_generic<W, T, I>(&self, mut iter: I, config: &Config, handle: &mut BufWriter<W>)
+ where
+ W: Write,
+ T: Writeable + Copy,
+ I: Iterator<Item = T>,
+ {
+ if self.is_reverse_range() && !self.has_negative_index() {
+ self.print_choice_reverse(iter, config, handle);
} else if self.has_negative_index() {
- let vec = line_iter.collect::<Vec<&str>>();
- self.print_negative_choice(vec, config, handle);
+ self.print_choice_negative(iter, config, handle);
} else {
if self.start > 0 {
- line_iter.nth((self.start - 1).try_into().unwrap());
- }
-
- let mut peek_line_iter = line_iter.peekable();
- for i in self.start..=self.end {
- match peek_line_iter.next() {
- Some(s) => Choice::write_bytes(
- s.as_bytes(),
- config,
- handle,
- peek_line_iter.peek().is_some() && i != self.end,
- ),
- None => break,
- };
+ iter.nth((self.start - 1).try_into().unwrap());
}
+ let range = self.end.checked_sub(self.start).unwrap();
+ Choice::print_choice_loop_max_items(iter, config, handle, range);
}
}
- fn print_negative_choice<W: Write>(
- &self,
- vec: Vec<&str>,
+ fn print_choice_loop<W, T, I>(iter: I, config: &Config, handle: &mut BufWriter<W>)
+ where
+ W: Write,
+ T: Writeable + Copy,
+ I: Iterator<Item = T>,
+ {
+ Choice::print_choice_loop_max_items(iter, config, handle, isize::max_value());
+ }
+
+ fn print_choice_loop_max_items<W, T, I>(
+ iter: I,
config: &Config,
handle: &mut BufWriter<W>,
- ) {
- let start = if self.start >= 0 {
- self.start.try_into().unwrap()
- } else {
- vec.len()
- .checked_sub(self.start.abs().try_into().unwrap())
- .unwrap()
- };
+ max_items: isize,
+ ) where
+ W: Write,
+ T: Writeable + Copy,
+ I: Iterator<Item = T>,
+ {
+ let mut peek_iter = iter.peekable();
+ for i in 0..=max_items {
+ match peek_iter.next() {
+ Some(s) => {
+ handle.write_choice(s, config, peek_iter.peek().is_some() && i != max_items);
+ }
+ None => break,
+ };
+ }
+ }
- let end = if self.end >= 0 {
- self.end.try_into().unwrap()
- } else {
- vec.len()
- .checked_sub(self.end.abs().try_into().unwrap())
- .unwrap()
- };
+ fn print_choice_negative<W, T, I>(&self, iter: I, config: &Config, handle: &mut BufWriter<W>)
+ where
+ W: Write,
+ T: Writeable + Copy,
+ I: Iterator<Item = T>,
+ {
+ let vec = Vec::from_iter(iter);
+ let (start, end) = self.get_negative_start_end(&vec);
if end > start {
for word in vec[start..std::cmp::min(end, vec.len() - 1)].iter() {
- Choice::write_bytes(word.as_bytes(), config, handle, true);
+ handle.write_choice(*word, config, true);
}
- Choice::write_bytes(
- vec[std::cmp::min(end, vec.len() - 1)].as_bytes(),
- config,
- handle,
- false,
- );
+ handle.write_choice(vec[std::cmp::min(end, vec.len() - 1)], config, false);
} else if self.start < 0 {
for word in vec[end + 1..=std::cmp::min(start, vec.len() - 1)]
.iter()
.rev()
{
- Choice::write_bytes(word.as_bytes(), config, handle, true);
+ handle.write_choice(*word, config, true);
}
- Choice::write_bytes(vec[end].as_bytes(), config, handle, false);
+ handle.write_choice(vec[end], config, false);
}
}
- fn write_bytes<WriterType: Write>(
- b: &[u8],
- config: &Config,
- handle: &mut BufWriter<WriterType>,
- print_separator: bool,
- ) {
- let num_bytes_written = match handle.write(b) {
- Ok(x) => x,
- Err(e) => {
- eprintln!("Failed to write to output: {}", e);
- 0
+ fn print_choice_reverse<W, T, I>(&self, mut iter: I, config: &Config, handle: &mut BufWriter<W>)
+ where
+ W: Write,
+ T: Writeable + Copy,
+ I: Iterator<Item = T>,
+ {
+ if self.end > 0 {
+ iter.nth((self.end - 1).try_into().unwrap());
+ }
+
+ let mut stack = Vec::new();
+ for i in 0..=(self.start - self.end) {
+ match iter.next() {
+ Some(s) => stack.push(s),
+ None => break,
}
- };
- if num_bytes_written > 0 && print_separator {
- Choice::write_separator(config, handle);
- };
- }
- pub fn write_separator<W: Write>(config: &Config, handle: &mut BufWriter<W>) {
- match handle.write(&config.output_separator) {
- Ok(_) => (),
- Err(e) => eprintln!("Failed to write to output: {}", e),
+ if self.start <= self.end + i {
+ break;
+ }
}
- }
- pub fn is_reverse_range(&self) -> bool {
- self.reversed
+ let mut peek_iter = stack.iter().rev().peekable();
+ loop {
+ match peek_iter.next() {
+ Some(s) => handle.write_choice(*s, config, peek_iter.peek().is_some()),
+ None => break,
+ }
+ }
}
- pub fn has_negative_index(&self) -> bool {
- self.negative_index
+ fn get_negative_start_end<T>(&self, vec: &Vec<T>) -> (usize, usize) {
+ let start = if self.start >= 0 {
+ self.start.try_into().unwrap()
+ } else {
+ vec.len()
+ .checked_sub(self.start.abs().try_into().unwrap())
+ .unwrap()
+ };
+
+ let end = if self.end >= 0 {
+ self.end.try_into().unwrap()
+ } else {
+ vec.len()
+ .checked_sub(self.end.abs().try_into().unwrap())
+ .unwrap()
+ };
+
+ return (start, end);
}
}
@@ -662,6 +680,76 @@ mod tests {
config.opt.choice[0].print_choice(&String::from("a b c d"), &config, &mut handle);
assert_eq!(String::from("abc"), MockStdout::str_from_buf_writer(handle));
}
+
+ #[test]
+ fn print_0_to_2_character_wise() {
+ let config = Config::from_iter(vec!["choose", "0:2", "-c"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(&String::from("abcd\n"), &config, &mut handle);
+ assert_eq!(String::from("abc"), MockStdout::str_from_buf_writer(handle));
+ }
+
+ #[test]
+ fn print_2_to_end_character_wise() {
+ let config = Config::from_iter(vec!["choose", "2:", "-c"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(&String::from("abcd\n"), &config, &mut handle);
+ assert_eq!(String::from("cd"), MockStdout::str_from_buf_writer(handle));
+ }
+
+ #[test]
+ fn print_start_to_2_character_wise() {
+ let config = Config::from_iter(vec!["choose", ":2", "-c"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(&String::from("abcd\n"), &config, &mut handle);
+ assert_eq!(String::from("abc"), MockStdout::str_from_buf_writer(handle));
+ }
+
+ #[test]
+ fn print_0_to_2_character_wise_exclusive() {
+ let config = Config::from_iter(vec!["choose", "0:2", "-c", "-x"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(&String::from("abcd\n"), &config, &mut handle);
+ assert_eq!(String::from("ab"), MockStdout::str_from_buf_writer(handle));
+ }
+
+ #[test]
+ fn print_0_to_2_character_wise_with_output_delimeter() {
+ let config = Config::from_iter(vec!["choose", "0:2", "-c", "-o", ":"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(&String::from("abcd\n"), &config, &mut handle);
+ assert_eq!(
+ String::from("a:b:c"),
+ MockStdout::str_from_buf_writer(handle)
+ );
+ }
+
+ #[test]
+ fn print_after_end_character_wise() {
+ let config = Config::from_iter(vec!["choose", "0:9", "-c"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(&String::from("abcd\n"), &config, &mut handle);
+ assert_eq!(
+ String::from("abcd"),
+ MockStdout::str_from_buf_writer(handle)
+ );
+ }
+
+ #[test]
+ fn print_2_to_0_character_wise() {
+ let config = Config::from_iter(vec!["choose", "2:0", "-c"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(&String::from("abcd\n"), &config, &mut handle);
+ assert_eq!(String::from("cba"), MockStdout::str_from_buf_writer(handle));
+ }
+
+ #[test]
+ fn print_neg_2_to_end_character_wise() {
+ let config = Config::from_iter(vec!["choose", "-2:", "-c"]);
+ let mut handle = BufWriter::new(MockStdout::new());
+ config.opt.choice[0].print_choice(&String::from("abcd\n"), &config, &mut handle);
+ assert_eq!(String::from("cd"), MockStdout::str_from_buf_writer(handle));
+ }
}
mod is_reverse_range_tests {