use std::{ cell::RefCell, fs::File, io::{self, BufRead, BufReader}, mem, path::{Path, PathBuf}, rc::Rc, sync::OnceLock, }; use crate::tui::{ model::{LogEntry, RawLogEntry}, processing::LogStream, }; struct Inner { pub path: PathBuf, file: BufReader, cached_entries: Vec>, } #[derive(Clone)] pub struct LogfileReader(Rc>); impl LogfileReader { pub fn new(p: &Path) -> io::Result { Ok(Self(Rc::new(RefCell::new(Inner { file: BufReader::new(File::open(p)?), path: p.to_path_buf(), cached_entries: Vec::new(), })))) } pub fn path(&self) -> PathBuf { self.0.borrow().path.clone() } fn next_line(&mut self) -> Option { let mut res = String::new(); match self.0.borrow_mut().file.read_line(&mut res) { Err(e) => { eprintln!("error: {e:?}"); None } Ok(0) => None, Ok(_) => Some(res), } } fn next_raw_entry(&mut self) -> Option { let line = self.next_line()?; match serde_json::from_str(&line) { Ok(i) => Some(i), Err(e) => { eprintln!("deserializing: {e:?} in {line}"); None } } } fn next_entry(&mut self) -> Option> { let mut stack = Vec::<(RawLogEntry, Vec>)>::new(); let mut curr = Vec::>::new(); loop { let entry = self.next_raw_entry()?; let new_entry = Rc::new(match entry.fields.message() { Some("enter") => { stack.push((entry, mem::take(&mut curr))); continue; } Some("exit") => { // TODO: does it match? let Some((enter, prev)) = stack.pop() else { panic!("exit before entry"); }; let sub_entries = mem::replace(&mut curr, prev); LogEntry::Sub { enter: enter, sub_entries, exit: entry, count_sub: OnceLock::new(), } } _ => LogEntry::Single { raw: entry }, }); if stack.is_empty() { return Some(new_entry); } else { curr.push(new_entry); } } } fn fill_buf_to_access_index(&mut self, n: usize) -> Option> { while self.0.borrow().cached_entries.len() <= n { let entry = self.next_entry()?; self.0.borrow_mut().cached_entries.push(entry); } Some(Rc::clone(&self.0.borrow().cached_entries[n])) } pub fn iter(&self) -> LogFileReaderStream { LogFileReaderStream { reader: self.clone(), curr: 0, } } } pub struct LogFileReaderStream { reader: LogfileReader, curr: usize, } impl LogStream for LogFileReaderStream { fn next(&mut self) -> Option<(Rc, usize)> { let entry = self.reader.fill_buf_to_access_index(self.curr)?; self.curr += 1; Some((entry, 0)) } fn prev(&mut self) -> Option<(Rc, usize)> { if self.curr == 0 { return None; } let entry = self.reader.fill_buf_to_access_index(self.curr)?; self.curr -= 1; Some((entry, 0)) } fn clone(&self) -> Box { Box::new(Self { reader: self.reader.clone(), curr: self.curr, }) } }