1#![warn(missing_docs)]
6#![forbid(unsafe_code)]
7use crossbeam_channel::Sender;
8use log::LevelFilter;
9use std::env;
10use std::fmt::Display;
11use std::path::Path;
12use std::sync::Once;
13
14#[macro_export]
16macro_rules! watch {
17 ( $( $x:expr ),* ) => {
18 {
20 $(
21 ::log::trace!("{}: {}", stringify!($x), format!("{:#?}", $x).replace("\\n", "\n"));
22 )*
23 }
24 };
25}
26
27#[macro_export]
29macro_rules! error {
30 ($fmt:literal $(, $x:expr )* ) => {
31 {
32 ::log::error!($fmt $(,$x)*);
33 }
34 };
35 ( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
36 {
37 (&$channel).send(Some(::xvc_logging::XvcOutputLine::Error(format!($fmt $(, $x)*)))).unwrap();
38 }
39 };
40}
41
42#[macro_export]
44macro_rules! info {
45 ($fmt:literal $(, $x:expr )* ) => {
46 {
47 ::log::info!($fmt $(, $x)*);
48 }
49 };
50 ( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
51 {
52 (&$channel).send(Some(::xvc_logging::XvcOutputLine::Info(format!($fmt $(,$x)*)))).unwrap();
53 }
54 };
55}
56
57#[macro_export]
59macro_rules! warn {
60 ($fmt:literal $(, $x:expr )* ) => {
61 {
62 ::log::warn!($fmt $(, $x)*);
63 }
64 };
65 ( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
66 {
67 (&$channel).send(
68 Some(
69 ::xvc_logging::XvcOutputLine::Warn(
70 format!($fmt $(, $x)*)))).unwrap();
71 }
72 };
73}
74
75#[macro_export]
77macro_rules! debug {
78 ($fmt:literal $(, $x:expr )* ) => {
79 {
80 ::log::debug!($fmt $(, $x)*);
81 }
82 };
83 ( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
84 {
85 (&$channel).send(Some(::xvc_logging::XvcOutputLine::Debug(format!($fmt $(, $x)*)))).unwrap();
86 }
87 };
88}
89
90#[macro_export]
92macro_rules! trace {
93 ($fmt:literal $(, $x:expr )* ) => {
94 {
95 ::log::trace!($fmt $(, $x)*);
96 }
97 };
98 ( $channel:expr, $fmt:literal $(, $x:expr ),* ) => {
99 {
100 (&$channel).send(Some(::xvc_logging::XvcOutputLine::Trace(format!("{} [{}::{}]", format!($fmt $(, $x)*), file!(), line!())))).unwrap();
101 }
102 };
103
104 ( $( $x:expr ),* ) => {
105 {
106 $(
107 ::log::trace!("{}: {}", stringify!($x), format!("{:#?}", $x).replace("\\n", "\n"));
108 )*
109 }
110 };
111}
112
113#[macro_export]
115macro_rules! output {
116 ($fmt:literal $(, $x:expr )* ) => {
117 {
118 ::std::println!($fmt $(, $x)*);
119 }
120 };
121 ( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
122 {
123 (&$channel).send(Some(::xvc_logging::XvcOutputLine::Output(format!($fmt $(, $x)*)))).unwrap();
124 }
125 };
126}
127
128#[macro_export]
130macro_rules! panic {
131 ($fmt:literal $(, $x:expr )* ) => {
132 {
133 watch!($fmt $(, $x)*);
134 ::std::panic!($fmt $(, $x)*);
135 }
136 };
137 ( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
138 {
139 (&$channel).send(Some(::xvc_logging::XvcOutputLine::Panic(format!("{} [{}::{}]",
140 format!($fmt $(, $x)*), file!(), line!())))).unwrap();
141 ::std::panic!($fmt $(, $x)*);
142 }
143 };
144}
145
146#[macro_export]
148macro_rules! tick {
149 ( $channel:ident, $n:literal) => {{
150 (&$channel)
151 .send(Some(::xvc_logging::XvcOutputLine::Tick($n)))
152 .unwrap();
153 }};
154 ($n:literal) => {{
155 for _ in 0..$n {
156 ::std::print!(".");
157 }
158 }};
159}
160
161#[macro_export]
165macro_rules! uwr {
166 ( $e:expr, $channel:expr ) => {{
167 match $e {
168 Ok(v) => v,
169 Err(e) => {
170 (&$channel)
171 .send(Some(::xvc_logging::XvcOutputLine::Panic(format!(
172 "{:?}, [{}::{}]",
173 e,
174 file!(),
175 line!()
176 ))))
177 .unwrap();
178 ::std::panic!("{:?}", e);
179 }
180 }
181 }};
182}
183
184#[macro_export]
188macro_rules! uwo {
189 ( $e:expr, $channel:expr ) => {{
190 match $e {
191 Some(v) => v,
192 None => {
193 watch!($e);
194 let msg = format!(
195 "None from the expression: {} [{}::{}]",
196 stringify!($e),
197 file!(),
198 line!()
199 );
200 (&$channel)
201 .send(Some(::xvc_logging::XvcOutputLine::Panic(msg.clone())))
202 .unwrap();
203 ::std::panic!("{}", msg);
204 }
205 }
206 }};
207}
208
209static INIT: Once = Once::new();
211
212pub fn setup_logging(term_level: Option<LevelFilter>, file_level: Option<LevelFilter>) {
215 INIT.call_once(|| init_logging(term_level, file_level));
216}
217
218fn init_logging(term_level: Option<LevelFilter>, file_level: Option<LevelFilter>) {
219 let logfilename = &format!("{}/xvc.log", env::temp_dir().to_string_lossy(),);
220
221 let logfile = Path::new(&logfilename);
222
223 let mut dispatch = fern::Dispatch::new().format(|out, message, record| {
224 out.finish(format_args!(
225 "[{}][{}::{}] {}",
226 record.level(),
227 record.file().get_or_insert("None"),
228 record.line().get_or_insert(0),
229 message
230 ))
231 });
232
233 if let Some(level) = term_level {
234 dispatch = dispatch.level(level).chain(std::io::stderr());
235 }
236
237 if let Some(level) = file_level {
238 dispatch = dispatch
239 .level(level)
240 .chain(fern::log_file(logfilename).expect("Cannot set log filename"));
241 }
242
243 match dispatch.apply() {
244 Ok(_) => {
245 if let Some(level) = term_level {
246 debug!("Terminal logger enabled with level: {:?}", level);
247 };
248 if let Some(level) = file_level {
249 debug!(
250 "File logger enabled with level: {:?} to {:?}",
251 level, logfile
252 );
253 };
254 }
255 Err(err) => {
256 error!("Error enabling logger: {:?}", err);
257 }
258 };
259}
260
261#[derive(Clone, Debug)]
263pub enum XvcOutputLine {
264 Output(String),
266 Info(String),
268 Debug(String),
270 Warn(String),
272 Error(String),
274 Panic(String),
277 Tick(usize),
280}
281
282pub type XvcOutputSender = Sender<Option<XvcOutputLine>>;
284
285impl XvcOutputLine {
286 pub fn info(s: &str) -> Self {
288 Self::Info(s.to_string())
289 }
290 pub fn debug(s: &str) -> Self {
292 Self::Debug(s.to_string())
293 }
294 pub fn warn(s: &str) -> Self {
296 Self::Warn(s.to_string())
297 }
298
299 pub fn error(s: &str) -> Self {
301 Self::Error(s.to_string())
302 }
303
304 pub fn panic(s: &str) -> Self {
308 Self::Panic(s.to_string())
309 }
310
311 pub fn tick(n: usize) -> Self {
313 Self::Tick(n)
314 }
315}
316
317impl From<&str> for XvcOutputLine {
318 fn from(s: &str) -> Self {
319 Self::Output(s.to_string())
320 }
321}
322
323impl From<String> for XvcOutputLine {
324 fn from(s: String) -> Self {
325 Self::Output(s)
326 }
327}
328
329impl Display for XvcOutputLine {
330 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331 match &self {
332 XvcOutputLine::Output(s) => writeln!(f, "{}", s),
333 XvcOutputLine::Info(s) => writeln!(f, "[INFO] {}", s),
334 XvcOutputLine::Debug(s) => writeln!(f, "[DEBUG] {}", s),
335 XvcOutputLine::Warn(s) => writeln!(f, "[WARN] {}", s),
336 XvcOutputLine::Error(s) => writeln!(f, "[ERROR] {}", s),
337 XvcOutputLine::Panic(s) => writeln!(f, "[PANIC] {}", s),
338 XvcOutputLine::Tick(n) => write!(f, "{}", ".".repeat(*n)),
339 }
340 }
341}