use std::convert::TryInto;
use crate::config::Config;
use crate::io::{BufWriter, Write};
#[derive(Debug)]
pub struct Choice {
pub start: isize,
pub end: isize,
negative_index: bool,
reversed: bool,
}
impl Choice {
pub fn new(start: isize, end: isize) -> Self {
let negative_index = start < 0 || end < 0;
let reversed = end < start;
Choice {
start,
end,
negative_index,
reversed,
}
}
pub fn print_choice<W: Write>(
&self,
line: &String,
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());
}
let mut stack = Vec::new();
for i in 0..=(self.start - self.end) {
match line_iter.next() {
Some(s) => stack.push(s),
None => break,
}
if self.start <= self.end + i {
break;
}
}
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,
}
}
} else if self.has_negative_index() {
let vec = line_iter.collect::<Vec<&str>>();
self.print_negative_choice(vec, 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,
};
}
}
}
fn print_negative_choice<W: Write>(
&self,
vec: Vec<&str>,
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()
};
let end = if self.end >= 0 {
self.end.try_into().unwrap()
} else {
vec.len()
.checked_sub(self.end.abs().try_into().unwrap())
.unwrap()
};
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);
}
Choice::write_bytes(
vec[std::cmp::min(end, vec.len() - 1)].as_bytes(),
config,
handle,
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);
}
Choice::write_bytes(vec[end].as_bytes(), config, handle, 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
}
};
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),
}
}
pub fn is_reverse_range(&self) -> bool {
self.reversed
}
pub fn has_negative_index(&self) -> bool {
self.negative_index
}
}
#[cfg(test)]
mod tests {
use crate::config::Config;
use crate::opt::Opt;
use std::ffi::OsString;