cursive_logger_view/
formatter.rs1use crate::{
2 log_buffer::{self, GET_LOCK_ERR_MSG},
3 CursiveLogWriter, LogItems,
4};
5use compact_str::{format_compact, CompactString, ToCompactString};
6use cursive_core::{
7 theme::{BaseColor, Color},
8 utils::markup::StyledString,
9};
10use flexi_logger::{writers::LogWriter, DeferredNow, Record};
11use getset::WithSetters;
12use log::Level;
13use std::{io, thread};
14use tap::Pipe;
15
16const fn log_level_as_dark_color(level: &Level) -> Color {
17 use BaseColor::{Cyan, Green, Magenta, Red, Yellow};
18 use Level::*;
19 let base_color = match level {
20 Trace => Magenta,
21 Debug => Cyan,
22 Info => Green,
23 Warn => Yellow,
24 Error => Red,
25 };
26 Color::Dark(base_color)
27}
28
29#[derive(Debug, WithSetters)]
30struct StyledTextConfig<'a> {
31 line: &'a mut StyledString,
32 #[getset(set_with)]
33 content: CompactString,
34 #[getset(set_with)]
35 color_enabled: bool,
36 color: Color,
37}
38
39impl StyledTextConfig<'_> {
40 fn append_line(mut self) -> Self {
41 let content = (&mut self.content) .pipe(core::mem::take);
43
44 match self.color_enabled {
45 true => self
46 .line
47 .append_styled(content, self.color),
48 _ => self.line.append_plain(content),
49 }
50 self
51 }
52
53 fn append_mod_line(self, rec: &Record) -> Self {
54 let (path, line_num) = (
55 rec.module_path().unwrap_or(""), rec.line().unwrap_or(0),
57 );
58
59 format_compact!("<{path}:") .pipe(|s| self.line.append_plain(s));
61
62 format_compact!("{line_num}") .pipe(|s| {
64 self.line
65 .append_styled(s, Color::Dark(BaseColor::Blue))
66 });
67
68 self.line.append_plain("> ");
69
70 self
71 }
72}
73
74impl LogWriter for CursiveLogWriter<'_> {
75 fn write(&self, now: &mut DeferredNow, record: &Record) -> io::Result<()> {
76 let styled_config = StyledTextConfig {
77 line: &mut StyledString::new(),
78 content: "".into(),
79 color_enabled: false,
80 color: record
81 .level()
82 .pipe_ref(log_level_as_dark_color),
83 };
84
85 let line = self
86 .format
87 .iter()
88 .fold(styled_config, |cfg, item| {
89 use crate::LogItems::{
90 DateTime, File, FileLine, Level, Message, ModLine, Thread,
91 };
92
93 match item {
94 DateTime => now
95 .format(&self.time_format)
96 .pipe(|fmt| format_compact!("{fmt} "))
97 .pipe(|s| cfg.with_content(s))
98 .with_color_enabled(false)
99 .append_line(),
100 Thread => thread::current()
102 .name()
103 .unwrap_or(" ")
104 .pipe(|name| format_compact!("[{name}] "))
105 .pipe(|s| cfg.with_content(s))
106 .with_color_enabled(false)
107 .append_line(),
108 Level => record
110 .level()
111 .pipe(|lv| format_compact!("[{lv}] "))
112 .pipe(|s| cfg.with_content(s))
113 .with_color_enabled(true)
114 .append_line(),
115 File => record
117 .file()
118 .unwrap_or("")
119 .pipe(|file| format_compact!("<{file}> "))
120 .pipe(|s| cfg.with_content(s))
121 .with_color_enabled(false)
122 .append_line(),
123 FileLine => format_compact!(
125 "<{}:{}> ",
126 record.file().unwrap_or(""),
127 record.line().unwrap_or(0),
128 )
129 .pipe(|s| cfg.with_content(s))
130 .with_color_enabled(false)
131 .append_line(),
132 ModLine => cfg.append_mod_line(record),
134 Message => record
136 .args()
137 .pipe(|x| format_compact!("{x}"))
138 .pipe(|s| cfg.with_content(s))
139 .with_color_enabled(true)
140 .append_line(),
141 LogItems::Custom(txt) => cfg
143 .with_content(txt.to_compact_string())
144 .with_color_enabled(true)
145 .append_line(), }
147 })
148 .line
149 .pipe(core::mem::take);
150
151 log_buffer::static_logs()
152 .lock()
153 .expect(GET_LOCK_ERR_MSG)
154 .push_back(line);
155
156 let io_broken_pipe = |msg| io::Error::new(io::ErrorKind::BrokenPipe, msg);
157
158 self.sink
159 .send(Box::new(|_| {}))
160 .map_err(|_| io_broken_pipe("cursive callback sink is closed!"))
161 }
162
163 fn flush(&self) -> io::Result<()> {
164 Ok(())
166 }
167
168 fn max_log_level(&self) -> log::LevelFilter {
169 log::LevelFilter::max()
170 }
171}