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