Skip to main content

euv_ui/component/vconsole/hook/
impl.rs

1use crate::*;
2
3/// Implements the Console struct providing web console API methods.
4///
5/// Each method outputs to both the browser developer console and the
6/// vConsole panel signal, with appropriate log level classification.
7/// Methods are associated functions that internally access the global
8/// Console instance, so callers never need to hold a reference.
9impl Console {
10    /// Initializes the global Console log signal.
11    ///
12    /// Must be called once during application startup before any `Console::log`,
13    /// `Console::warn`, or `Console::error` calls.
14    pub fn init() {
15        let signal: Signal<Vec<ConsoleEntry>> = Signal::create(Vec::new());
16        CONSOLE_LOG_SIGNAL.set(signal);
17    }
18
19    /// Logs an informational message (equivalent to console.log).
20    ///
21    /// # Arguments
22    ///
23    /// - `&str`: The message to log.
24    ///
25    /// # Panics
26    ///
27    /// Panics if `init_console` has not been called.
28    pub fn log(message: &str) {
29        console::log_1(&message.into());
30        Self::append_entry(ConsoleEntry::new(LogLevel::Log, message.to_string()));
31    }
32
33    /// Logs a warning message (equivalent to console.warn).
34    ///
35    /// # Arguments
36    ///
37    /// - `&str`: The warning message to log.
38    ///
39    /// # Panics
40    ///
41    /// Panics if `init_console` has not been called.
42    pub fn warn(message: &str) {
43        console::warn_1(&message.into());
44        Self::append_entry(ConsoleEntry::new(LogLevel::Warn, message.to_string()));
45    }
46
47    /// Logs an error message (equivalent to console.error).
48    ///
49    /// # Arguments
50    ///
51    /// - `&str`: The error message to log.
52    ///
53    /// # Panics
54    ///
55    /// Panics if `init_console` has not been called.
56    pub fn error(message: &str) {
57        console::error_1(&message.into());
58        Self::append_entry(ConsoleEntry::new(LogLevel::Error, message.to_string()));
59    }
60
61    /// Clears all log entries from the vConsole panel signal.
62    ///
63    /// # Panics
64    ///
65    /// Panics if `init_console` has not been called.
66    pub fn clear() {
67        let log: Signal<Vec<ConsoleEntry>> = Self::get_signal();
68        log.set(Vec::new());
69    }
70
71    /// Returns the global vConsole log signal.
72    ///
73    /// # Returns
74    ///
75    /// - `Signal<Vec<ConsoleEntry>>`: The console log signal.
76    ///
77    /// # Panics
78    ///
79    /// Panics if `init_console` has not been called.
80    pub(crate) fn get_signal() -> Signal<Vec<ConsoleEntry>> {
81        CONSOLE_LOG_SIGNAL.get()
82    }
83
84    /// Creates a click event handler that opens the vConsole fab panel.
85    ///
86    /// Pushes an overlay state and sets the panel visibility signal to true.
87    ///
88    /// # Arguments
89    ///
90    /// - `Signal<bool>`: The signal controlling panel visibility.
91    ///
92    /// # Returns
93    ///
94    /// - `Option<Rc<dyn Fn(Event)>>`: A click handler that opens the panel.
95    pub(crate) fn fab_on_click(panel_open: Signal<bool>) -> Option<Rc<dyn Fn(Event)>> {
96        Some(Rc::new(move |_: Event| {
97            let closer: Rc<dyn Fn()> = Rc::new(move || {
98                panel_open.set(false);
99            });
100            Router::overlay_stack_push(closer);
101            panel_open.set(true);
102        }))
103    }
104
105    /// Filters and reverses console log entries based on the current filter signal value.
106    ///
107    /// # Arguments
108    ///
109    /// - `Signal<Vec<ConsoleEntry>>`: The console log signal.
110    /// - `Signal<LogFilter>`: The current filter level signal.
111    ///
112    /// # Returns
113    ///
114    /// - `Vec<(usize, ConsoleEntry)>`: The filtered and reversed entries with original indices.
115    pub(crate) fn filter_entries(
116        logs: Signal<Vec<ConsoleEntry>>,
117        filter: Signal<LogFilter>,
118    ) -> Vec<(usize, ConsoleEntry)> {
119        let log_list: Vec<ConsoleEntry> = logs.get();
120        let filter_value: LogFilter = filter.get();
121        let mut result: Vec<(usize, ConsoleEntry)> = log_list
122            .iter()
123            .enumerate()
124            .filter(|(_, entry): &(usize, &ConsoleEntry)| match filter_value {
125                LogFilter::All => true,
126                LogFilter::Log => entry.get_level() == LogLevel::Log,
127                LogFilter::Warn => entry.get_level() == LogLevel::Warn,
128                LogFilter::Error => entry.get_level() == LogLevel::Error,
129            })
130            .map(|(index, entry): (usize, &ConsoleEntry)| (index, entry.clone()))
131            .collect();
132        result.reverse();
133        result
134    }
135
136    /// Appends an entry to the vConsole log signal, trimming if over capacity.
137    ///
138    /// # Arguments
139    ///
140    /// - `ConsoleEntry`: The console entry to append.
141    ///
142    /// # Panics
143    ///
144    /// Panics if `init_console` has not been called.
145    fn append_entry(entry: ConsoleEntry) {
146        let log: Signal<Vec<ConsoleEntry>> = Self::get_signal();
147        let mut current: Vec<ConsoleEntry> = log.get();
148        current.push(entry);
149        if current.len() > MAX_CONSOLE_LOG_ENTRIES {
150            let excess: usize = current.len() - MAX_CONSOLE_LOG_ENTRIES;
151            current.drain(0..excess);
152        }
153        log.set(current);
154    }
155}
156
157/// Implements the Display trait for LogFilter to render filter button labels.
158impl std::fmt::Display for LogFilter {
159    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160        let label: &str = match self {
161            LogFilter::All => "All",
162            LogFilter::Log => "Log",
163            LogFilter::Warn => "Warn",
164            LogFilter::Error => "Error",
165        };
166        write!(formatter, "{}", label)
167    }
168}
169
170/// Implementation of log level badge rendering.
171impl LogLevel {
172    /// Returns the short badge label for a log level.
173    ///
174    /// # Returns
175    ///
176    /// - `&str`: The badge label string ("LOG", "WRN", "ERR").
177    pub(crate) fn badge(self) -> &'static str {
178        match self {
179            LogLevel::Log => "LOG",
180            LogLevel::Warn => "WRN",
181            LogLevel::Error => "ERR",
182        }
183    }
184}
185
186/// Implementation of log filter event handlers.
187impl LogFilter {
188    /// Creates a click event handler that sets the log filter to "All".
189    ///
190    /// # Arguments
191    ///
192    /// - `Signal<LogFilter>`: The signal controlling the active log filter.
193    ///
194    /// # Returns
195    ///
196    /// - `Option<Rc<dyn Fn(Event)>>`: A click handler that sets filter to All.
197    pub(crate) fn on_filter_all(filter_signal: Signal<LogFilter>) -> Option<Rc<dyn Fn(Event)>> {
198        Some(Rc::new(move |_: Event| {
199            filter_signal.set(LogFilter::All);
200        }))
201    }
202
203    /// Creates a click event handler that sets the log filter to "Log".
204    ///
205    /// # Arguments
206    ///
207    /// - `Signal<LogFilter>`: The signal controlling the active log filter.
208    ///
209    /// # Returns
210    ///
211    /// - `Option<Rc<dyn Fn(Event)>>`: A click handler that sets filter to Log.
212    pub(crate) fn on_filter_log(filter_signal: Signal<LogFilter>) -> Option<Rc<dyn Fn(Event)>> {
213        Some(Rc::new(move |_: Event| {
214            filter_signal.set(LogFilter::Log);
215        }))
216    }
217
218    /// Creates a click event handler that sets the log filter to "Warn".
219    ///
220    /// # Arguments
221    ///
222    /// - `Signal<LogFilter>`: The signal controlling the active log filter.
223    ///
224    /// # Returns
225    ///
226    /// - `Option<Rc<dyn Fn(Event)>>`: A click handler that sets filter to Warn.
227    pub(crate) fn on_filter_warn(filter_signal: Signal<LogFilter>) -> Option<Rc<dyn Fn(Event)>> {
228        Some(Rc::new(move |_: Event| {
229            filter_signal.set(LogFilter::Warn);
230        }))
231    }
232
233    /// Creates a click event handler that sets the log filter to "Error".
234    ///
235    /// # Arguments
236    ///
237    /// - `Signal<LogFilter>`: The signal controlling the active log filter.
238    ///
239    /// # Returns
240    ///
241    /// - `Option<Rc<dyn Fn(Event)>>`: A click handler that sets filter to Error.
242    pub(crate) fn on_filter_error(filter_signal: Signal<LogFilter>) -> Option<Rc<dyn Fn(Event)>> {
243        Some(Rc::new(move |_: Event| {
244            filter_signal.set(LogFilter::Error);
245        }))
246    }
247}