1use std::io;
16use std::sync::Arc;
17use std::sync::Mutex;
18use std::sync::RwLock;
19
20use once_cell::sync::Lazy;
21use slog::Drain;
22use slog::Level;
23
24#[derive(PartialEq, Copy, Clone)]
25pub enum TargetLog {
26 All,
27 File,
28 Term,
29}
30
31pub static LOG_TARGET: Lazy<Arc<RwLock<TargetLog>>> =
32 Lazy::new(|| Arc::new(RwLock::new(TargetLog::All)));
33
34pub fn get_current_log_target() -> TargetLog {
35 *LOG_TARGET
36 .read()
37 .expect("Failed to acquire read lock on the LOG_TARGET")
38}
39
40pub fn set_current_log_target(target: TargetLog) {
41 let mut log_target = LOG_TARGET
42 .write()
43 .expect("Failed to acquire write lock on the LOG_TARGET");
44 *log_target = target;
45}
46
47pub struct CPMsgRecord {
50 level: Level,
51 msg: String,
52 consumed: bool,
53}
54
55impl CPMsgRecord {
56 pub fn get_msg(&mut self) -> Option<String> {
57 if self.consumed {
58 None
59 } else {
60 self.consumed = true;
61 Some(Self::construct_msg(self.level, &self.msg))
62 }
63 }
64
65 pub fn construct_msg(level: Level, msg: &str) -> String {
70 format!("{}: {}", level.as_str(), msg)
71 }
72
73 fn set_msg(&mut self, msg: String, level: Level) {
74 self.msg = msg;
75 self.level = level;
76 self.consumed = false;
77 }
78
79 fn new() -> Self {
80 Self {
81 level: Level::Trace,
82 msg: String::new(),
83 consumed: true,
84 }
85 }
86}
87
88pub static LAST_LOG_TO_DISPLAY: Lazy<Arc<Mutex<CPMsgRecord>>> =
99 Lazy::new(|| Arc::new(Mutex::new(CPMsgRecord::new())));
100
101pub fn get_last_log_to_display() -> Option<String> {
102 LAST_LOG_TO_DISPLAY
103 .lock()
104 .expect("Fail to acquire lock for LAST_LOG_TO_DISPLAY")
105 .get_msg()
106}
107
108pub struct CompoundDecorator<W: io::Write, T: io::Write> {
109 file: Arc<Mutex<W>>,
110 term: Arc<Mutex<T>>,
111}
112
113impl<W, T> CompoundDecorator<W, T>
114where
115 W: io::Write,
116 T: io::Write,
117{
118 pub fn new(file_io: W, term_io: T) -> Self {
119 Self {
120 file: Arc::new(Mutex::new(file_io)),
121 term: Arc::new(Mutex::new(term_io)),
122 }
123 }
124}
125
126impl<W, T> slog_term::Decorator for CompoundDecorator<W, T>
127where
128 W: io::Write,
129 T: io::Write,
130{
131 fn with_record<F>(
132 &self,
133 _record: &slog::Record,
134 _logger_values: &slog::OwnedKVList,
135 f: F,
136 ) -> io::Result<()>
137 where
138 F: FnOnce(&mut dyn slog_term::RecordDecorator) -> io::Result<()>,
139 {
140 f(&mut CompoundRecordDecorator(
141 &*self.file,
142 &*self.term,
143 *LOG_TARGET
144 .read()
145 .expect("Failed to acquire write lock on the LOG_TARGET"),
146 ))
147 }
148}
149
150pub struct CompoundRecordDecorator<'a, W: 'a, T: 'a>(&'a Mutex<W>, &'a Mutex<T>, TargetLog)
151where
152 W: io::Write,
153 T: io::Write;
154
155impl<'a, W, T> io::Write for CompoundRecordDecorator<'a, W, T>
156where
157 W: io::Write,
158 T: io::Write,
159{
160 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
161 match self.2 {
162 TargetLog::All => {
163 let term_res = self.1.lock().unwrap().write(buf);
164 let file_res = self.0.lock().unwrap().write(buf);
165 term_res?;
166 file_res
167 }
168 TargetLog::File => self.0.lock().unwrap().write(buf),
169 TargetLog::Term => self.1.lock().unwrap().write(buf),
170 }
171 }
172
173 fn flush(&mut self) -> io::Result<()> {
174 let term_res = self.1.lock().unwrap().flush();
175 let file_res = self.0.lock().unwrap().flush();
176 term_res?;
177 file_res
178 }
179}
180
181impl<'a, W, T> Drop for CompoundRecordDecorator<'a, W, T>
182where
183 W: io::Write,
184 T: io::Write,
185{
186 fn drop(&mut self) {
187 let _ = self.1.lock().unwrap().flush();
188 let _ = self.0.lock().unwrap().flush();
189 }
190}
191
192impl<'a, W, T> slog_term::RecordDecorator for CompoundRecordDecorator<'a, W, T>
193where
194 W: io::Write,
195 T: io::Write,
196{
197 fn reset(&mut self) -> io::Result<()> {
198 Ok(())
199 }
200}
201
202pub struct CommandPaletteDrain<D> {
203 drain: D,
204}
205
206impl<D> CommandPaletteDrain<D> {
207 pub fn new(drain: D) -> Self {
208 Self { drain }
209 }
210}
211
212impl<D> Drain for CommandPaletteDrain<D>
213where
214 D: Drain,
215{
216 type Ok = Option<D::Ok>;
217 type Err = Option<D::Err>;
218
219 fn log(
220 &self,
221 record: &slog::Record,
222 values: &slog::OwnedKVList,
223 ) -> std::result::Result<Self::Ok, Self::Err> {
224 if record.tag() == "V" {
226 LAST_LOG_TO_DISPLAY
227 .lock()
228 .expect("Fail to acquire write lock for LAST_LOG_TO_DISPLAY")
229 .set_msg(format!("{}", record.msg()), record.level());
230 }
231 self.drain.log(record, values).map(Some).map_err(Some)
232 }
233}
234
235pub fn get_logger() -> slog::Logger {
236 let plain = slog_term::PlainSyncDecorator::new(std::io::stderr());
237 slog::Logger::root(slog_term::FullFormat::new(plain).build().fuse(), slog::o!())
238}