1pub use backtrace;
2#[cfg(feature = "impl")]
3pub use chrono;
4#[cfg(feature = "impl")]
5pub use fern;
6#[cfg(feature = "impl")]
7pub use libc;
8#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
9#[allow(unused_imports)]
10pub use log;
11
12pub use log::*;
13pub use anyhow::{self, Error, Result, Context, Chain, format_err, ensure, bail};
14
15#[derive(Debug)]
16pub struct Position {
17 pub file: String,
18 pub line: u32,
19 pub column: u32,
20}
21
22impl std::fmt::Display for Position {
23 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
24 write!(f, "{}:{}:{}", self.file, self.line, self.column)
25 }
26}
27
28#[macro_export]
29macro_rules! diag_position {
30 () => {{
31 $crate::Position {
32 file: std::path::PathBuf::from(file!()).file_name().unwrap().to_string_lossy().to_string(),
33 line: line!(),
34 column: column!(),
35 }
36 }};
37}
38
39#[macro_export]
40macro_rules! diag {
41 ($($arg:tt)+) => {{
42 $crate::error!(target: "diagnostics", $($arg)*);
43 }};
44}
45
46#[macro_export]
47macro_rules! diag_backtrace {
48 () => {{
49 $crate::error!(target: "diagnostics", "{:?}", $crate::backtrace::Backtrace::new());
50 }}
51}
52
53#[macro_export]
54macro_rules! diag_err {
55 () => {{
56 $crate::diag!("internal error at {}", $crate::diag_position!());
57 $crate::diag_backtrace!();
58 $crate::anyhow::anyhow!("internal error")
59 }};
60 ($($arg:tt)+) => {{
61 $crate::diag!("internal error at {}", $crate::diag_position!());
62 $crate::diag!($($arg)*);
63 $crate::diag_backtrace!();
64 $crate::anyhow::anyhow!($($arg)*)
65 }}
66}
67
68#[macro_export]
69macro_rules! bail_diag {
70 () => {{
71 $crate::diag!("internal error at {}", $crate::diag_position!());
72 $crate::diag_backtrace!();
73 $crate::anyhow::bail!("internal error");
74 }};
75 ($($arg:tt)+) => {{
76 $crate::diag!("internal error at {}", $crate::diag_position!());
77 $crate::diag!($($arg)*);
78 $crate::diag_backtrace!();
79 $crate::anyhow::bail!($($arg)*);
80 }}
81}
82
83#[macro_export]
84macro_rules! diag_unreachable {
85 () => {{
86 debug_assert!(false, "unreachable code reached");
87 $crate::diag!("unreachable code reached at {}", $crate::diag_position!());
88 $crate::diag_backtrace!();
89 }};
90 ($($arg:tt)+) => {{
91 debug_assert!(false, $($arg)*);
92 $crate::diag!($($arg)*);
93 $crate::diag_backtrace!();
94 }}
95}
96
97#[macro_export]
98macro_rules! diag_unreachable_err {
99 () => {{
100 $crate::diag_unreachable!();
101 $crate::anyhow::anyhow!("unreachable code reached at {}", $crate::diag_position!())
102 }};
103 ($($arg:tt)+) => {{
104 $crate::diag_unreachable!($($arg)*);
105 $crate::anyhow::anyhow!($($arg)*)
106 }}
107}
108
109#[macro_export]
110macro_rules! diag_unimplemented {
111 () => {{
112 debug_assert!(false, "unimplemented code reached");
113 $crate::diag!("unimplemented code reached at {}", $crate::diag_position!());
114 $crate::diag_backtrace!();
115 }};
116 ($($arg:tt)+) => {{
117 debug_assert!(false, $($arg)*);
118 $crate::diag!($($arg)*);
119 $crate::diag_backtrace!();
120 }}
121}
122
123#[macro_export]
124macro_rules! diag_unimplemented_err {
125 () => {{
126 $crate::diag_unreachable!();
127 $crate::anyhow::anyhow!("unimplemented code reached at {}", $crate::diag_position!())
128 }};
129 ($($arg:tt)+) => {{
130 $crate::diag_unreachable!($($arg)*);
131 $crate::anyhow::anyhow!($($arg)*)
132 }}
133}
134
135#[cfg(feature = "impl")]
136pub fn stdout_dispatch() -> fern::Dispatch {
137 use fern::colors::Color;
138 let colors = fern::colors::ColoredLevelConfig::new()
139 .trace(Color::White)
140 .debug(Color::Blue)
141 .info(Color::Green)
142 .warn(Color::Yellow)
143 .error(Color::Red);
144 fern::Dispatch::new()
145 .format(move |out, message, record| {
146 out.finish(format_args!(
147 "{} {}{}: {}",
148 chrono::Local::now().format("[%Y-%m-%d %H:%M:%S]"),
149 if atty::is(atty::Stream::Stdout) {
150 format!("{}", colors.color(record.level()))
151 } else {
152 format!("{}", record.level())
153 },
154 if record.level() == log::Level::Info || record.level() == log::Level::Warn {
155 " "
156 } else {
157 ""
158 },
159 message,
160 ))
161 })
162 .level(log::LevelFilter::Info)
163 .level_for("diagnostics", log::LevelFilter::Off)
164}
165
166#[cfg(feature = "impl")]
167pub fn stdout_dispatch_with_target() -> fern::Dispatch {
168 use fern::colors::Color;
169 let colors = fern::colors::ColoredLevelConfig::new()
170 .trace(Color::White)
171 .debug(Color::Blue)
172 .info(Color::Green)
173 .warn(Color::Yellow)
174 .error(Color::Red);
175 fern::Dispatch::new()
176 .format(move |out, message, record| {
177 out.finish(format_args!(
178 "{} {}{} {}: {}",
179 chrono::Local::now().format("[%Y-%m-%d %H:%M:%S]"),
180 colors.color(record.level()),
181 if record.level() == log::Level::Info || record.level() == log::Level::Warn {
182 " "
183 } else {
184 ""
185 },
186 record.target(),
187 message,
188 ))
189 })
190 .level(log::LevelFilter::Info)
191 .level_for("diagnostics", log::LevelFilter::Off)
192}
193
194#[cfg(feature = "impl")]
195pub fn diag_dispatch() -> fern::Dispatch {
196 fern::Dispatch::new()
197 .format(move |out, message, record| {
198 out.finish(format_args!(
199 "{} {}{} {}: {}",
200 chrono::Local::now().format("[%Y-%m-%d %H:%M:%S%.9f]"),
201 record.level(),
202 if record.level() == log::Level::Info {
203 " "
204 } else {
205 ""
206 },
207 record.target(),
208 message,
209 ))
210 })
211 .level(log::LevelFilter::Info)
212 .level_for("diagnostics", log::LevelFilter::Trace)
213}
214
215#[cfg(feature = "impl")]
216pub fn init_logger(
217 log_file: Option<impl AsRef<std::path::Path>>,
218) -> std::io::Result<fern::Dispatch> {
219 let mut dispatch = fern::Dispatch::new().chain(stdout_dispatch().chain(std::io::stdout()));
220 let log_file = if let Some(log_file) = log_file {
221 Some(log_file.as_ref().to_owned())
222 } else if let Ok(exe_path) = std::env::current_exe() {
223 exe_path.parent().map(|exe_dir| exe_dir.join(".diag.log"))
224 } else {
225 None
226 };
227 if let Some(log_file) = log_file {
228 let log_file = std::fs::OpenOptions::new()
229 .write(true)
230 .create(true)
231 .truncate(true)
232 .open(log_file)?;
233 dispatch = dispatch.chain(diag_dispatch().chain(log_file))
234 }
235 Ok(dispatch)
236}
237
238#[cfg(test)]
239mod tests;