use std::collections::VecDeque;
use chrono::{DateTime, Utc};
use crate::ui::logs::LogLine;
pub struct LogBuffer {
lines: VecDeque<LogLine>,
capacity: usize,
}
impl LogBuffer {
pub fn new(capacity: usize) -> Self {
Self {
lines: VecDeque::with_capacity(capacity.min(1024)),
capacity,
}
}
pub fn push(&mut self, line: LogLine) {
if self.lines.len() >= self.capacity {
self.lines.pop_front();
}
self.lines.push_back(line);
}
pub fn tail(&self, n: usize) -> Vec<&LogLine> {
let skip = self.lines.len().saturating_sub(n);
self.lines.iter().skip(skip).collect()
}
pub fn since(&self, since: DateTime<Utc>) -> Vec<&LogLine> {
self.lines.iter().filter(|l| l.timestamp >= since).collect()
}
pub fn all(&self) -> Vec<&LogLine> {
self.lines.iter().collect()
}
pub fn len(&self) -> usize {
self.lines.len()
}
pub fn is_empty(&self) -> bool {
self.lines.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Duration;
fn make_line(service: &str, ts: DateTime<Utc>) -> LogLine {
LogLine {
timestamp: ts,
service: service.to_string(),
text: "test".to_string(),
is_stderr: false,
level: None,
}
}
#[test]
fn buffer_capacity_eviction() {
let mut buf = LogBuffer::new(3);
let now = Utc::now();
buf.push(make_line("a", now));
buf.push(make_line("b", now));
buf.push(make_line("c", now));
assert_eq!(buf.len(), 3);
buf.push(make_line("d", now));
assert_eq!(buf.len(), 3);
assert_eq!(buf.all()[0].service, "b"); }
#[test]
fn buffer_tail() {
let mut buf = LogBuffer::new(10);
let now = Utc::now();
for i in 0..5 {
buf.push(make_line(&format!("s{}", i), now));
}
let tail = buf.tail(3);
assert_eq!(tail.len(), 3);
assert_eq!(tail[0].service, "s2");
assert_eq!(tail[1].service, "s3");
assert_eq!(tail[2].service, "s4");
}
#[test]
fn buffer_since() {
let mut buf = LogBuffer::new(10);
let t0 = Utc::now() - Duration::seconds(60);
let t1 = Utc::now() - Duration::seconds(30);
let t2 = Utc::now();
buf.push(make_line("old", t0));
buf.push(make_line("mid", t1));
buf.push(make_line("new", t2));
let cutoff = Utc::now() - Duration::seconds(45);
let result = buf.since(cutoff);
assert_eq!(result.len(), 2);
assert_eq!(result[0].service, "mid");
assert_eq!(result[1].service, "new");
}
#[test]
fn buffer_empty() {
let buf = LogBuffer::new(10);
assert!(buf.is_empty());
assert_eq!(buf.len(), 0);
assert!(buf.all().is_empty());
assert!(buf.tail(5).is_empty());
}
}