p2p_chat/ui/state/logs.rs
1//! This module contains log-related functionalities for the `UIState`.
2use crate::ui::{log_entry::LogEntry, mode::UIMode};
3
4use super::UIState;
5
6impl UIState {
7 /// Adds a batch of new log entries to the UI state.
8 ///
9 /// This function appends new log entries to the buffer, enforcing `max_log_entries`.
10 /// If the UI is in log mode and at the bottom of the scroll, it resets the
11 /// scroll offset. Otherwise, it adjusts the scroll offset to keep new
12 /// messages visible if not explicitly scrolled up.
13 ///
14 /// # Arguments
15 ///
16 /// * `entries` - A `Vec` of `LogEntry` to add.
17 pub fn add_log_batch(&mut self, entries: Vec<LogEntry>) {
18 let new_entries_count = entries.len();
19
20 for entry in entries {
21 if self.logs.len() >= self.max_log_entries {
22 self.logs.pop_front();
23 }
24 self.logs.push_back(entry);
25 }
26
27 if matches!(self.mode, UIMode::Logs { .. }) {
28 if self.is_at_bottom_log {
29 self.log_scroll_offset = 0;
30 } else {
31 self.log_scroll_offset = self.log_scroll_offset.saturating_add(new_entries_count);
32 self.update_log_scroll_state(self.terminal_size.1 as usize);
33 }
34 }
35 }
36
37 /// Triggers a refresh of the log display.
38 ///
39 /// This function resets the log scroll offset and marks the view as being
40 /// at the bottom, useful when filter settings change.
41 pub fn refresh_logs(&mut self) {
42 if matches!(self.mode, UIMode::Logs { .. }) {
43 self.log_scroll_offset = 0;
44 self.is_at_bottom_log = true;
45 }
46 }
47
48 /// Updates the log scroll state based on the current terminal height.
49 ///
50 /// This ensures that the `log_scroll_offset` remains within valid bounds and
51 /// updates `is_at_bottom_log`.
52 ///
53 /// # Arguments
54 ///
55 /// * `terminal_height` - The current height of the terminal in lines.
56 pub fn update_log_scroll_state(&mut self, terminal_height: usize) {
57 let filtered_logs = self.filtered_logs();
58 let total_logs = filtered_logs.len();
59 let visible_lines = terminal_height.saturating_sub(2); // Account for input and status lines
60 let max_scroll = if total_logs > visible_lines {
61 total_logs.saturating_sub(visible_lines)
62 } else {
63 0
64 };
65
66 self.log_scroll_offset = self.log_scroll_offset.min(max_scroll);
67 self.is_at_bottom_log = self.log_scroll_offset == 0;
68 }
69
70 /// Scrolls the log view to the bottom.
71 pub fn jump_to_bottom_log(&mut self) {
72 self.log_scroll_offset = 0;
73 self.is_at_bottom_log = true;
74 }
75
76 /// Returns a vector of log entries filtered by the current `UIMode::Logs` settings.
77 ///
78 /// Logs can be filtered by minimum `Level` and by a text filter string,
79 /// which can include exclusions prefixed with `-`.
80 pub fn filtered_logs(&self) -> Vec<&LogEntry> {
81 match &self.mode {
82 UIMode::Logs { filter, level } => self
83 .logs
84 .iter()
85 .filter(|entry| {
86 entry.level <= *level
87 && filter
88 .as_ref()
89 .map(|f| {
90 if let Some(exclusion) = f.strip_prefix('-') {
91 !entry.module.contains(exclusion)
92 } else {
93 entry.module.contains(f) || entry.message.contains(f)
94 }
95 })
96 .unwrap_or(true)
97 })
98 .collect(),
99 _ => self.logs.iter().collect(),
100 }
101 }
102}