use std::io::{self, Write};
use std::path::PathBuf;
use std::time::Duration;
use bytes::{Buf, BytesMut};
use miette::{IntoDiagnostic, Result};
mod batch;
mod file_state;
pub mod watcher;
use crate::errors::TaleError;
use crate::logpatterns::*;
use crate::{config, process_line};
pub fn handle_static(paths: Vec<PathBuf>) -> Result<()> {
use file_state::FileStateManager;
let mut file_manager = FileStateManager::new();
for path in &paths {
file_manager.add_file(path)?;
}
let all_lines = file_manager.read_all_lines()?;
let mut all_sourced_lines: Vec<SourcedLine<'_>> = Vec::new();
all_lines.iter().for_each(|(file_path, lines)| {
lines.iter().enumerate().for_each(|(line_num, line_content)| {
let parsed: Printable<'_> = {
match serde_json::from_str::<Printable<'_>>(line_content) {
Ok(printable) => printable,
Err(_) => {
Printable::Text(line_content.to_owned())
}
}
};
let sourced = SourcedLine::new(parsed, file_path.clone(), line_num);
all_sourced_lines.push(sourced);
});
});
all_sourced_lines.sort_by_key(|line| line.sort_key());
let mut outlock = io::stdout().lock();
let mut buffer = BytesMut::with_capacity(2048);
for wrapped in all_sourced_lines {
wrapped.write(&mut buffer);
outlock.write_all(buffer.chunk()).into_diagnostic()?;
outlock.write_all(&[0x0a; 1]).into_diagnostic()?; buffer.clear();
}
outlock.flush().into_diagnostic()?;
Ok(())
}
pub async fn handle_tailing(paths: Vec<PathBuf>) -> Result<()> {
use batch::{BatchConfig, BatchedLine, batched_with_config};
use watcher::{WatchEvent, create_watcher};
let mut watcher = create_watcher();
watcher.add_files(paths).await?;
let batch_config = BatchConfig {
batch_window: Duration::from_millis(config::batch_window_ms()),
max_batch_size: 1000,
_max_buffer_memory: 10 * 1024 * 1024,
};
let mut batch_processor = batched_with_config(batch_config);
let (line_sender, mut batch_receiver) = batch_processor.start().await?;
let mut watch_events = watcher.watch().await?;
let mut outlock = io::stdout().lock();
let mut buffer = BytesMut::with_capacity(2048);
loop {
tokio::select! {
watch_event = watch_events.recv() => {
match watch_event {
Some(WatchEvent::FileModified(path)) => {
if let Some(state) = watcher.file_manager_mut().get_state_mut(&path)
&& let Ok(_changed) = state.refresh()
&& let Ok(new_lines) = state.read_new_lines() {
for (line_num, line) in new_lines.into_iter().enumerate() {
let batched_line = BatchedLine::new(
line,
path.clone(),
line_num as u64
);
match line_sender.send(batched_line) {
Ok(v) => v,
Err(_) => return Err(TaleError::BatchedLineSender.into())
}
}
}
}
Some(WatchEvent::Error(err)) => {
eprintln!("Watch error: {err}");
}
Some(_) => {
}
None => {
break;
}
}
}
batch = batch_receiver.recv() => {
match batch {
Some(sorted_lines) => {
for batched_line in sorted_lines {
process_line(&batched_line.content, &mut buffer, &mut outlock)?;
}
outlock.flush().into_diagnostic()?;
}
None => {
break;
}
}
}
}
}
Ok(())
}