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