//! `ripgrab tail` — follow mode entry point.
use std::io;
use anyhow::Result;
use crate::cli::args::TailArgs;
use crate::filter::dsl;
use crate::filter::FilterSet;
use crate::render::mode::stream::StreamRenderer;
use crate::source::file::FileSource;
use crate::source::stdin::StdinSource;
use crate::source::Source;
pub async fn run(args: TailArgs) -> Result<()> {
let filter = FilterSet::compile(&args.includes, &args.excludes, None)?;
let dsl_filter = args
.where_clause
.as_deref()
.map(dsl::parse_and_compile)
.transpose()?;
let mut sources: Vec<Box<dyn Source>> = Vec::with_capacity(args.paths.len());
for path in &args.paths {
if path.as_os_str() == "-" {
sources.push(Box::new(StdinSource::new()));
} else if args.from_end > 0 {
sources.push(Box::new(FileSource::follow(path).await?));
} else {
sources.push(Box::new(FileSource::open(path).await?));
}
}
let stdout = io::stdout();
let mut renderer = StreamRenderer::new(stdout.lock(), !args.no_origin, true);
for src in &mut sources {
while let Some(item) = src.next_line().await? {
if let Some(compiled) = &dsl_filter {
let mut ctx = crate::filter::dsl::EvalContext::new();
ctx.set("origin", &item.origin);
ctx.set("body", &item.line);
if !crate::filter::dsl::evaluate(compiled, &ctx) {
continue;
}
}
let outcome = filter.apply(&item.line);
match outcome {
crate::filter::MatchOutcome::Plain => {
renderer.render(&item.origin, &item.line)?;
}
crate::filter::MatchOutcome::Fields(fields) => {
let flat: String = fields
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join(" ");
renderer.render(&item.origin, &flat)?;
}
}
}
}
renderer.flush()?;
Ok(())
}