use log::debug;
use crate::common::LineReader;
use crate::common::LineWriter;
use crate::filter::between_args::MatchHandling;
use crate::filter::between_args::FROM_DEFAULT;
use crate::filter::BetweenArgs;
pub async fn between(
args: BetweenArgs,
reader: &mut impl LineReader,
writer: &mut impl LineWriter,
) -> Result<(), String> {
if args.from.as_str() == FROM_DEFAULT && args.to.is_none() {
return Err(
"between: at least one of --from or --to should be provided; see --help".to_owned(),
);
}
let mut i = 1;
let mut found_start = false;
while let Some(line) = reader.read_line().await {
if args.from.is_match(line) {
debug!(
"found a 'between' start match at line #{i}, handling={}",
args.from_handling
);
found_start = true;
if args.from_handling == MatchHandling::Include {
writer.write_line(line).await;
}
break;
}
i += 1;
}
if !found_start {
debug!("reached end of input in 'between' after {i} lines before finding start match; stopping");
return Ok(());
}
if let Some(end_re) = &args.to {
debug!("searching end pattern in 'between' from line #{i}");
while let Some(line) = reader.read_line().await {
if end_re.is_match(line) {
debug!(
"found a 'between' end match at line #{i}, handling={}",
args.to_handling
);
if args.to_handling == MatchHandling::Include {
writer.write_line(line).await;
}
return Ok(());
}
writer.write_line(line).await;
i += 1;
}
debug!("reached end of input in 'between' before finding end match after line #{i}");
} else {
debug!("no end pattern in 'between', returning all remaining lines from line #{i}");
while let Some(line) = reader.read_line().await {
writer.write_line(line).await;
i += 1;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use ::regex::Regex;
use crate::common::{CollectorWriter, VecReader};
use super::*;
async fn check_between_args<L: Into<String>>(args: BetweenArgs, lines: Vec<L>) -> Vec<String> {
let mut writer = CollectorWriter::new();
between(args, &mut VecReader::new(lines), &mut writer)
.await
.unwrap();
writer.lines().snapshot().await.clone()
}
async fn check_between<L: Into<String>>(lines: Vec<L>) -> Vec<String> {
let args = BetweenArgs {
from: Regex::new("start").unwrap(),
to: Some(Regex::new("end").unwrap()),
from_handling: MatchHandling::Include,
to_handling: MatchHandling::Exclude,
};
check_between_args(args, lines).await
}
#[async_std::test]
async fn start_match() {
let res = check_between(vec!["before", "start", "middle"]).await;
assert_eq!(res, vec!["start", "middle"]);
}
#[async_std::test]
async fn end_match() {
let res = check_between(vec!["middle", "end", "after"]).await;
assert!(res.is_empty());
}
#[async_std::test]
async fn no_start_or_end_match() {
let res = check_between(vec!["before", "middle", "after"]).await;
assert!(res.is_empty());
}
#[async_std::test]
async fn start_and_end_match() {
let res = check_between(vec!["before", "start", "middle", "end", "after"]).await;
assert_eq!(res, vec!["start", "middle"]);
}
#[async_std::test]
async fn start_and_end_match_rev_handling() {
let args = BetweenArgs {
from: Regex::new("start").unwrap(),
to: Some(Regex::new("end").unwrap()),
from_handling: MatchHandling::Exclude,
to_handling: MatchHandling::Include,
};
let res = check_between_args(args, vec!["before", "start", "middle", "end", "after"]).await;
assert_eq!(res, vec!["middle", "end"]);
}
#[async_std::test]
async fn start_and_end_same_line_match() {
let res = check_between(vec!["before", "start end", "after"]).await;
assert_eq!(res, vec!["start end", "after"]);
}
#[async_std::test]
async fn no_start_pattern() {
let args = BetweenArgs {
from: Regex::new(FROM_DEFAULT).unwrap(),
to: Some(Regex::new("end").unwrap()),
from_handling: MatchHandling::Include,
to_handling: MatchHandling::Include,
};
let res = check_between_args(args, vec!["before", "start", "middle", "end", "after"]).await;
assert_eq!(res, vec!["before", "start", "middle", "end"]);
}
#[async_std::test]
async fn empty_line_with_no_start_pattern() {
let args = BetweenArgs {
from: Regex::new(FROM_DEFAULT).unwrap(),
to: Some(Regex::new("end").unwrap()),
from_handling: MatchHandling::Include,
to_handling: MatchHandling::Include,
};
let res = check_between_args(args, vec!["", "line"]).await;
assert_eq!(res, vec!["", "line"]);
}
#[async_std::test]
async fn no_end_pattern() {
let args = BetweenArgs {
from: Regex::new("start").unwrap(),
to: None,
from_handling: MatchHandling::Exclude,
to_handling: MatchHandling::Exclude,
};
let res = check_between_args(args, vec!["before", "start", "middle", "end", "after"]).await;
assert_eq!(res, vec!["middle", "end", "after"]);
}
#[async_std::test]
async fn no_patterns() {
let args = BetweenArgs {
from: Regex::new(FROM_DEFAULT).unwrap(),
to: None,
from_handling: MatchHandling::Exclude,
to_handling: MatchHandling::Exclude,
};
let res = between(
args,
&mut VecReader::new(vec![""]),
&mut CollectorWriter::new(),
)
.await;
assert!(res.is_err());
}
}