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}