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