1#[cfg(all(debug_assertions))]
70mod debug {
71 use std::sync::Mutex;
72
73 use once_cell::sync::Lazy;
74
75 static DEBUG: Lazy<Mutex<Option<String>>> =
76 Lazy::new(|| Mutex::new(std::option_env!("DEBUG").map(|x| x.to_owned())));
77 static LEVELS: Mutex<Vec<String>> = Mutex::new(Vec::new());
78
79 pub fn set_debug(s: &str) {
81 *DEBUG.lock().unwrap() = Some(s.to_owned());
82 }
83
84 pub mod console {
85 #[cfg(all(feature = "wasm", target_arch = "wasm32"))]
86 use wasm_bindgen::prelude::wasm_bindgen;
87
88 #[cfg(all(feature = "wasm", target_arch = "wasm32"))]
89 #[wasm_bindgen]
90 extern "C" {
91 #[wasm_bindgen(js_namespace = console)]
94 pub fn log(s: &str);
95
96 #[wasm_bindgen(js_namespace = console)]
97 pub fn group(s: &str);
98
99 #[wasm_bindgen(js_namespace = console)]
100 pub fn groupEnd();
101 }
102
103 #[cfg(not(all(feature = "wasm", target_arch = "wasm32")))]
104 pub use patch::*;
105 #[cfg(not(all(feature = "wasm", target_arch = "wasm32")))]
106 mod patch {
107 pub fn log(s: &str) {
108 eprintln!("{}", s);
109 }
110
111 pub fn group(s: &str) {
112 eprintln!("{}", s);
113 }
114
115 pub fn groupEnd() {}
116 }
117 }
118
119 #[doc(hidden)]
120 #[macro_export]
121 macro_rules! inner_println {
122 ($($arg:tt)+) => {{
123 if $crate::should_log(&file!()) {
124 if cfg!(all(feature = "wasm", target_arch = "wasm32")) {
125 let s = format!($($arg)+);
126 $crate::console::log(&s);
127 } else {
128 eprintln!($($arg)+);
129 }
130 }
131 }};
132 () => {
133 if $crate::should_log(&file!()) {
134 if cfg!(all(feature = "wasm", target_arch = "wasm32")) {
135 $crate::console::log("");
136 } else {
137 eprintln!();
138 }
139 }
140 };
141 }
142
143 #[doc(hidden)]
144 pub fn get_level() -> usize {
145 LEVELS.lock().unwrap().len()
146 }
147
148 #[doc(hidden)]
149 pub fn indent(name: &str) {
150 let space = format!("{}", " ".repeat(get_level()));
151 inner_println!("{}{} {{", space, name);
152 LEVELS.lock().unwrap().push(name.to_string())
153 }
154
155 #[doc(hidden)]
156 pub fn outdent() {
157 LEVELS.lock().unwrap().pop();
158 let space = format!("{}", " ".repeat(get_level()));
159 inner_println!("{}}}", space);
160 }
161
162 #[doc(hidden)]
163 pub fn dbg<T: std::fmt::Debug>(value: T, name: &str, line: &str) {
164 let s = format!("{:#?}", value);
165 let mut ans = String::new();
166 ans.push_str(&" ".repeat(get_level()));
167 ans.push_str(format!("[{}] {} = ", line, name).as_str());
168 for (i, line) in s.split('\n').enumerate() {
169 if i != 0 {
170 ans.push_str(&" ".repeat(get_level()));
171 }
172 ans.push_str(line);
173 ans.push('\n')
174 }
175
176 if ans.ends_with('\n') {
177 ans.drain(ans.len() - 1..);
178 }
179
180 inner_println!("{}", ans);
181 }
182
183 #[doc(hidden)]
184 pub fn prepend_indent(s: String) -> String {
185 let mut ans = String::new();
186 for (i, line) in s.split('\n').enumerate() {
187 if i != 0 {
188 ans.push_str(&" ".repeat(get_level()));
189 }
190 ans.push_str(line);
191 ans.push('\n')
192 }
193 ans
194 }
195
196 #[doc(hidden)]
197 pub fn should_log(file: &str) -> bool {
198 let lock = DEBUG.lock().unwrap();
199 lock.as_ref()
200 .map_or(false, |x| !x.is_empty() && (x == "*" || file.contains(x)))
201 }
202
203 #[macro_export]
205 macro_rules! group {
206 ($($arg:tt)*) => {
207 let __debug_log_group_guard = {
208 let line = format!("{}:{}", file!(), line!());
209 let mut guard = None;
210 if $crate::should_log(&line) {
211 $crate::indent(&format!($($arg)*));
212 guard = Some($crate::GroupGuard);
213 }
214 guard
215 };
216 };
217 () => {
218 let mut __debug_log_group_guard= None;
219 if $crate::should_log(&file!()) {
220 $crate::indent("".to_string());
221 __debug_log_group_guard = Some($crate::GroupGuard);
222 }
223 };
224 }
225
226 #[doc(hidden)]
227 pub struct GroupGuard;
228 impl Drop for GroupGuard {
229 fn drop(&mut self) {
230 crate::outdent();
231 }
232 }
233
234 #[macro_export]
236 macro_rules! debug_dbg {
237 ($($val:expr),+ $(,)?) => {
238 let line = format!("{}:{}", file!(), line!());
239 if $crate::should_log(&line) {
240 ($($crate::dbg($val, stringify!($val), &line)),+,);
241 }
242 };
243 () => {
244 let line = format!("{}:{}", file!(), line!());
245 if $crate::should_log(&line) {
246 let space = format!("{}", " ".repeat($crate::get_level()));
247 $crate::inner_println!("{}[{}] ",space, line);
248 }
249 }
250 }
251
252 #[macro_export]
254 macro_rules! debug_log {
255 ($($arg:tt)*) => {{
256 let line = format!("{}:{}", file!(), line!());
257 if $crate::should_log(&line) {
258 let prefix = format!("{}[{}] ", " ".repeat($crate::get_level()), line);
259 let s = format!($($arg)*);
260 $crate::inner_println!("{}{}", prefix, $crate::prepend_indent(s));
261 }
262 }};
263 () => {
264 if $crate::should_log(&file!()) {
265 $crate::inner_println();
266 }
267 };
268 }
269}
270
271#[cfg(not(debug_assertions))]
272mod debug {
273 pub fn set_debug(s: &str) {}
274
275 #[macro_export]
277 macro_rules! group {
278 ($($arg:tt)*) => {};
279 () => {};
280 }
281
282 #[macro_export]
284 macro_rules! debug_log {
285 ($($arg:tt)*) => {{}};
286 () => {};
287 }
288
289 #[macro_export]
291 macro_rules! debug_dbg {
292 ($($val:expr),+ $(,)?) => {};
293 () => {};
294 }
295
296 #[doc(hidden)]
297 pub struct GroupGuard;
298}
299
300pub use debug::*;
301
302#[cfg(test)]
303mod tests {
304 use crate::{debug_dbg, debug_log, group};
305
306 #[test]
307 fn it_works() {
310 group!("A Group");
311 group!("C Group");
312 {
313 group!("Sub A Group");
314 let arr: Vec<_> = (0..3).collect();
315 debug_dbg!(&arr);
316 {
317 group!("Sub Sub A Group");
318 debug_dbg!(&arr);
319 }
320 debug_log!("Hi");
321 debug_dbg!(&arr);
322 }
323
324 {
325 group!("B Group");
326 debug_log!("END");
327 }
328 }
329}