// channel_log.rs — Per-channel message history // // Shared by all channel daemon implementations. Tracks messages // with consumed/unread semantics for the recv protocol. use std::collections::VecDeque; const DEFAULT_CAPACITY: usize = 200; /// Per-channel message history with consumed/unread tracking. pub struct ChannelLog { messages: VecDeque, consumed: usize, total: usize, } impl ChannelLog { pub fn new() -> Self { Self { messages: VecDeque::with_capacity(DEFAULT_CAPACITY), consumed: 0, total: 0, } } pub fn push(&mut self, line: String) { if self.messages.len() >= DEFAULT_CAPACITY { self.messages.pop_front(); if self.consumed > 0 { self.consumed -= 1; } } self.messages.push_back(line); self.total += 1; } pub fn unread(&self) -> u32 { (self.total - self.consumed) as u32 } /// Return all unconsumed messages (marks consumed), plus scrollback /// to reach at least min_count total. pub fn recv_new(&mut self, min_count: usize) -> String { let buf_len = self.messages.len(); let unconsumed_start = buf_len.saturating_sub(self.total - self.consumed); let new_msgs: Vec<&str> = self.messages.iter() .skip(unconsumed_start) .map(|s| s.as_str()) .collect(); let need_extra = min_count.saturating_sub(new_msgs.len()); let scroll_start = unconsumed_start.saturating_sub(need_extra); let scrollback: Vec<&str> = self.messages.iter() .skip(scroll_start) .take(unconsumed_start - scroll_start) .map(|s| s.as_str()) .collect(); self.consumed = self.total; let mut result = scrollback; result.extend(new_msgs); result.join("\n") } /// Return last N lines without consuming. pub fn recv_history(&self, count: usize) -> String { self.messages.iter() .rev().take(count) .collect::>().into_iter().rev() .map(|s| s.as_str()) .collect::>() .join("\n") } }