1use crate::widget::logformatter::LogFormatter;
2use parking_lot::Mutex;
3use std::sync::Arc;
4use unicode_segmentation::UnicodeSegmentation;
5
6use log::LevelFilter;
7use ratatui::{
8 buffer::Buffer,
9 layout::{Constraint, Direction, Layout, Rect},
10 style::Style,
11 text::Line,
12 widgets::{Block, BorderType, Borders, Widget},
13};
14
15use crate::logger::TuiLoggerLevelOutput;
16use crate::logger::TUI_LOGGER;
17use crate::{TuiLoggerTargetWidget, TuiWidgetState};
18
19use super::{inner::TuiWidgetInnerState, standard::TuiLoggerWidget};
20
21pub struct TuiLoggerSmartWidget<'a> {
26 title_log: Line<'a>,
27 title_target: Line<'a>,
28 style: Option<Style>,
29 border_style: Style,
30 border_type: BorderType,
31 highlight_style: Option<Style>,
32 logformatter: Option<Box<dyn LogFormatter>>,
33 style_error: Option<Style>,
34 style_warn: Option<Style>,
35 style_debug: Option<Style>,
36 style_trace: Option<Style>,
37 style_info: Option<Style>,
38 style_show: Option<Style>,
39 style_hide: Option<Style>,
40 style_off: Option<Style>,
41 format_separator: Option<char>,
42 format_timestamp: Option<Option<String>>,
43 format_output_level: Option<Option<TuiLoggerLevelOutput>>,
44 format_output_target: Option<bool>,
45 format_output_file: Option<bool>,
46 format_output_line: Option<bool>,
47 state: Arc<Mutex<TuiWidgetInnerState>>,
48}
49impl<'a> Default for TuiLoggerSmartWidget<'a> {
50 fn default() -> Self {
51 TuiLoggerSmartWidget {
52 title_log: Line::from("Tui Log"),
53 title_target: Line::from("Tui Target Selector"),
54 style: None,
55 border_style: Style::default(),
56 border_type: BorderType::Plain,
57 highlight_style: None,
58 logformatter: None,
59 style_error: None,
60 style_warn: None,
61 style_debug: None,
62 style_trace: None,
63 style_info: None,
64 style_show: None,
65 style_hide: None,
66 style_off: None,
67 format_separator: None,
68 format_timestamp: None,
69 format_output_level: None,
70 format_output_target: None,
71 format_output_file: None,
72 format_output_line: None,
73 state: Arc::new(Mutex::new(TuiWidgetInnerState::new())),
74 }
75 }
76}
77impl<'a> TuiLoggerSmartWidget<'a> {
78 pub fn highlight_style(mut self, style: Style) -> Self {
79 self.highlight_style = Some(style);
80 self
81 }
82 pub fn border_style(mut self, style: Style) -> Self {
83 self.border_style = style;
84 self
85 }
86 pub fn border_type(mut self, border_type: BorderType) -> Self {
87 self.border_type = border_type;
88 self
89 }
90 pub fn style(mut self, style: Style) -> Self {
91 self.style = Some(style);
92 self
93 }
94 pub fn style_error(mut self, style: Style) -> Self {
95 self.style_error = Some(style);
96 self
97 }
98 pub fn style_warn(mut self, style: Style) -> Self {
99 self.style_warn = Some(style);
100 self
101 }
102 pub fn style_info(mut self, style: Style) -> Self {
103 self.style_info = Some(style);
104 self
105 }
106 pub fn style_trace(mut self, style: Style) -> Self {
107 self.style_trace = Some(style);
108 self
109 }
110 pub fn style_debug(mut self, style: Style) -> Self {
111 self.style_debug = Some(style);
112 self
113 }
114 pub fn style_off(mut self, style: Style) -> Self {
115 self.style_off = Some(style);
116 self
117 }
118 pub fn style_hide(mut self, style: Style) -> Self {
119 self.style_hide = Some(style);
120 self
121 }
122 pub fn style_show(mut self, style: Style) -> Self {
123 self.style_show = Some(style);
124 self
125 }
126 pub fn output_separator(mut self, sep: char) -> Self {
129 self.format_separator = Some(sep);
130 self
131 }
132 pub fn output_timestamp(mut self, fmt: Option<String>) -> Self {
139 self.format_timestamp = Some(fmt);
140 self
141 }
142 pub fn output_level(mut self, level: Option<TuiLoggerLevelOutput>) -> Self {
150 self.format_output_level = Some(level);
151 self
152 }
153 pub fn output_target(mut self, enabled: bool) -> Self {
157 self.format_output_target = Some(enabled);
158 self
159 }
160 pub fn output_file(mut self, enabled: bool) -> Self {
164 self.format_output_file = Some(enabled);
165 self
166 }
167 pub fn output_line(mut self, enabled: bool) -> Self {
171 self.format_output_line = Some(enabled);
172 self
173 }
174 pub fn title_target<T>(mut self, title: T) -> Self
175 where
176 T: Into<Line<'a>>,
177 {
178 self.title_target = title.into();
179 self
180 }
181 pub fn title_log<T>(mut self, title: T) -> Self
182 where
183 T: Into<Line<'a>>,
184 {
185 self.title_log = title.into();
186 self
187 }
188 pub fn state(mut self, state: &TuiWidgetState) -> Self {
189 self.state = state.clone_state();
190 self
191 }
192}
193impl<'a> Widget for TuiLoggerSmartWidget<'a> {
194 fn render(self, area: Rect, buf: &mut Buffer) {
196 let entries_s = {
197 let mut tui_lock = TUI_LOGGER.inner.lock();
198 let first_timestamp = tui_lock
199 .events
200 .iter()
201 .next()
202 .map(|entry| entry.timestamp.timestamp_millis());
203 let last_timestamp = tui_lock
204 .events
205 .rev_iter()
206 .next()
207 .map(|entry| entry.timestamp.timestamp_millis());
208 if let Some(first) = first_timestamp {
209 if let Some(last) = last_timestamp {
210 let dt = last - first;
211 if dt > 0 {
212 tui_lock.events.len() as f64 / (dt as f64) * 1000.0
213 } else {
214 0.0
215 }
216 } else {
217 0.0
218 }
219 } else {
220 0.0
221 }
222 };
223
224 let mut title_log = self.title_log.clone();
225 title_log
226 .spans
227 .push(format!(" [log={:.1}/s]", entries_s).into());
228
229 let hide_target = self.state.lock().hide_target;
230 if hide_target {
231 let tui_lw = TuiLoggerWidget::default()
232 .block(
233 Block::default()
234 .title(title_log)
235 .border_style(self.border_style)
236 .border_type(self.border_type)
237 .borders(Borders::ALL),
238 )
239 .opt_style(self.style)
240 .opt_style_error(self.style_error)
241 .opt_style_warn(self.style_warn)
242 .opt_style_info(self.style_info)
243 .opt_style_debug(self.style_debug)
244 .opt_style_trace(self.style_trace)
245 .opt_output_separator(self.format_separator)
246 .opt_output_timestamp(self.format_timestamp)
247 .opt_output_level(self.format_output_level)
248 .opt_output_target(self.format_output_target)
249 .opt_output_file(self.format_output_file)
250 .opt_output_line(self.format_output_line)
251 .inner_state(self.state);
252 tui_lw.render(area, buf);
253 } else {
254 let mut width: usize = 0;
255 {
256 let hot_targets = &TUI_LOGGER.inner.lock().targets;
257 let mut state = self.state.lock();
258 let hide_off = state.hide_off;
259 {
260 let targets = &mut state.config;
261 targets.merge(hot_targets);
262 for (t, levelfilter) in targets.iter() {
263 if hide_off && levelfilter == &LevelFilter::Off {
264 continue;
265 }
266 width = width.max(t.graphemes(true).count())
267 }
268 }
269 }
270 let chunks = Layout::default()
271 .direction(Direction::Horizontal)
272 .constraints(vec![
273 Constraint::Length(width as u16 + 6 + 2),
274 Constraint::Min(10),
275 ])
276 .split(area);
277 let tui_ltw = TuiLoggerTargetWidget::default()
278 .block(
279 Block::default()
280 .title(self.title_target)
281 .border_style(self.border_style)
282 .border_type(self.border_type)
283 .borders(Borders::ALL),
284 )
285 .opt_style(self.style)
286 .opt_highlight_style(self.highlight_style)
287 .opt_style_off(self.style_off)
288 .opt_style_hide(self.style_hide)
289 .opt_style_show(self.style_show)
290 .inner_state(self.state.clone());
291 tui_ltw.render(chunks[0], buf);
292 let tui_lw = TuiLoggerWidget::default()
293 .block(
294 Block::default()
295 .title(title_log)
296 .border_style(self.border_style)
297 .border_type(self.border_type)
298 .borders(Borders::ALL),
299 )
300 .opt_formatter(self.logformatter)
301 .opt_style(self.style)
302 .opt_style_error(self.style_error)
303 .opt_style_warn(self.style_warn)
304 .opt_style_info(self.style_info)
305 .opt_style_debug(self.style_debug)
306 .opt_style_trace(self.style_trace)
307 .opt_output_separator(self.format_separator)
308 .opt_output_timestamp(self.format_timestamp)
309 .opt_output_level(self.format_output_level)
310 .opt_output_target(self.format_output_target)
311 .opt_output_file(self.format_output_file)
312 .opt_output_line(self.format_output_line)
313 .inner_state(self.state.clone());
314 tui_lw.render(chunks[1], buf);
315 }
316 }
317}