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