p2p_chat/logging/
buffer.rs1use crate::ui::{LogEntry, UIMode};
6use std::collections::VecDeque;
7use std::sync::{Arc, Mutex};
8use std::time::Duration;
9use tokio::sync::mpsc;
10use tokio::time::{interval, Interval};
11use tracing::Level;
12
13pub struct LogBuffer {
15 entries: Arc<Mutex<VecDeque<LogEntry>>>,
17 max_size: usize,
19 ui_sender: Arc<Mutex<Option<mpsc::UnboundedSender<crate::ui::UIEvent>>>>,
21 current_display_level: Arc<Mutex<Level>>,
23 current_ui_mode: Arc<Mutex<UIMode>>,
25 pending_batch: Arc<Mutex<Vec<LogEntry>>>,
27 batch_timer: Arc<Mutex<Option<Interval>>>,
29}
30
31impl LogBuffer {
32 pub fn new(max_size: usize) -> Self {
38 Self {
39 entries: Arc::new(Mutex::new(VecDeque::with_capacity(max_size))),
40 max_size,
41 ui_sender: Arc::new(Mutex::new(None)),
42 current_display_level: Arc::new(Mutex::new(Level::DEBUG)),
43 current_ui_mode: Arc::new(Mutex::new(UIMode::Chat)),
44 pending_batch: Arc::new(Mutex::new(Vec::new())),
45 batch_timer: Arc::new(Mutex::new(None)),
46 }
47 }
48
49 pub fn set_ui_sender(&self, sender: mpsc::UnboundedSender<crate::ui::UIEvent>) {
53 *self.ui_sender.lock().unwrap() = Some(sender);
54 }
55
56 pub fn add_entry(&self, entry: LogEntry) {
61 {
63 let mut entries = self.entries.lock().unwrap();
64 if entries.len() >= self.max_size {
65 entries.pop_front();
66 }
67 entries.push_back(entry.clone());
68 }
69
70 let should_notify = {
72 let current_level = *self.current_display_level.lock().unwrap();
73 entry.level <= current_level
74 };
75
76 if should_notify {
77 self.pending_batch.lock().unwrap().push(entry);
79
80 self.start_batch_timer_if_needed();
82 }
83 }
84
85 pub fn set_display_level(&self, level: Level) {
87 *self.current_display_level.lock().unwrap() = level;
88
89 if let Some(ref sender) = *self.ui_sender.lock().unwrap() {
91 let _ = sender.send(crate::ui::UIEvent::RefreshLogs);
92 }
93 }
94
95 pub fn set_ui_mode(&self, mode: UIMode) {
97 *self.current_ui_mode.lock().unwrap() = mode.clone();
98
99 if matches!(mode, UIMode::Logs { .. }) {
101 if let Some(ref sender) = *self.ui_sender.lock().unwrap() {
102 let _ = sender.send(crate::ui::UIEvent::RefreshLogs);
103 }
104 }
105 }
106
107 fn start_batch_timer_if_needed(&self) {
109 let mut timer_guard = self.batch_timer.lock().unwrap();
110 if timer_guard.is_none() {
111 *timer_guard = Some(interval(Duration::from_millis(100))); let ui_sender = self.ui_sender.clone();
115 let pending_batch = self.pending_batch.clone();
116 let batch_timer = self.batch_timer.clone();
117
118 drop(timer_guard); tokio::spawn(async move {
122 let mut timer = {
123 let mut timer_guard = batch_timer.lock().unwrap();
124 timer_guard.take().unwrap()
125 };
126
127 timer.tick().await; loop {
130 timer.tick().await;
131
132 let batch = {
134 let mut pending = pending_batch.lock().unwrap();
135 if pending.is_empty() {
136 continue;
137 }
138 let batch = pending.drain(..).collect::<Vec<_>>();
139 batch
140 };
141
142 if let Some(ref sender) = *ui_sender.lock().unwrap() {
144 if sender.send(crate::ui::UIEvent::NewLogBatch(batch)).is_err() {
145 break;
147 }
148 } else {
149 break;
151 }
152 }
153
154 *batch_timer.lock().unwrap() = None;
156 });
157 }
158 }
159}