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}