#[cfg(all(debug_assertions))]
mod debug {
use std::sync::Mutex;
use once_cell::sync::Lazy;
static DEBUG: Lazy<Mutex<Option<String>>> =
Lazy::new(|| Mutex::new(std::option_env!("DEBUG").map(|x| x.to_owned())));
static LEVELS: Mutex<Vec<String>> = Mutex::new(Vec::new());
pub fn set_debug(s: &str) {
*DEBUG.lock().unwrap() = Some(s.to_owned());
}
pub mod console {
#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
use wasm_bindgen::prelude::wasm_bindgen;
#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
#[wasm_bindgen(js_namespace = console)]
pub fn group(s: &str);
#[wasm_bindgen(js_namespace = console)]
pub fn groupEnd();
}
#[cfg(not(all(feature = "wasm", target_arch = "wasm32")))]
pub use patch::*;
#[cfg(not(all(feature = "wasm", target_arch = "wasm32")))]
mod patch {
pub fn log(s: &str) {
eprintln!("{}", s);
}
pub fn group(s: &str) {
eprintln!("{}", s);
}
pub fn groupEnd() {}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! inner_println {
($($arg:tt)+) => {{
if $crate::should_log(&file!()) {
if cfg!(all(feature = "wasm", target_arch = "wasm32")) {
let s = format!($($arg)+);
$crate::console::log(&s);
} else {
eprintln!($($arg)+);
}
}
}};
() => {
if $crate::should_log(&file!()) {
if cfg!(all(feature = "wasm", target_arch = "wasm32")) {
$crate::console::log("");
} else {
eprintln!();
}
}
};
}
#[doc(hidden)]
pub fn get_level() -> usize {
LEVELS.lock().unwrap().len()
}
#[doc(hidden)]
pub fn indent(name: &str) {
let space = format!("{}", " ".repeat(get_level()));
inner_println!("{}{} {{", space, name);
LEVELS.lock().unwrap().push(name.to_string())
}
#[doc(hidden)]
pub fn outdent() {
LEVELS.lock().unwrap().pop();
let space = format!("{}", " ".repeat(get_level()));
inner_println!("{}}}", space);
}
#[doc(hidden)]
pub fn dbg<T: std::fmt::Debug>(value: T, name: &str, line: &str) {
let s = format!("{:#?}", value);
let mut ans = String::new();
ans.push_str(&" ".repeat(get_level()));
ans.push_str(format!("[{}] {} = ", line, name).as_str());
for (i, line) in s.split('\n').enumerate() {
if i != 0 {
ans.push_str(&" ".repeat(get_level()));
}
ans.push_str(line);
ans.push('\n')
}
if ans.ends_with('\n') {
ans.drain(ans.len() - 1..);
}
inner_println!("{}", ans);
}
#[doc(hidden)]
pub fn prepend_indent(s: String) -> String {
let mut ans = String::new();
for (i, line) in s.split('\n').enumerate() {
if i != 0 {
ans.push_str(&" ".repeat(get_level()));
}
ans.push_str(line);
ans.push('\n')
}
ans
}
#[doc(hidden)]
pub fn should_log(file: &str) -> bool {
let lock = DEBUG.lock().unwrap();
lock.as_ref()
.map_or(false, |x| !x.is_empty() && (x == "*" || file.contains(x)))
}
#[macro_export]
macro_rules! group {
($($arg:tt)*) => {
let __debug_log_group_guard = {
let line = format!("{}:{}", file!(), line!());
let mut guard = None;
if $crate::should_log(&line) {
$crate::indent(&format!($($arg)*));
guard = Some($crate::GroupGuard);
}
guard
};
};
() => {
let mut __debug_log_group_guard= None;
if $crate::should_log(&file!()) {
$crate::indent("".to_string());
__debug_log_group_guard = Some($crate::GroupGuard);
}
};
}
#[doc(hidden)]
pub struct GroupGuard;
impl Drop for GroupGuard {
fn drop(&mut self) {
crate::outdent();
}
}
#[macro_export]
macro_rules! debug_dbg {
($($val:expr),+ $(,)?) => {
let line = format!("{}:{}", file!(), line!());
if $crate::should_log(&line) {
($($crate::dbg($val, stringify!($val), &line)),+,);
}
};
() => {
let line = format!("{}:{}", file!(), line!());
if $crate::should_log(&line) {
let space = format!("{}", " ".repeat($crate::get_level()));
$crate::inner_println!("{}[{}] ",space, line);
}
}
}
#[macro_export]
macro_rules! debug_log {
($($arg:tt)*) => {{
let line = format!("{}:{}", file!(), line!());
if $crate::should_log(&line) {
let prefix = format!("{}[{}] ", " ".repeat($crate::get_level()), line);
let s = format!($($arg)*);
$crate::inner_println!("{}{}", prefix, $crate::prepend_indent(s));
}
}};
() => {
if $crate::should_log(&file!()) {
$crate::inner_println();
}
};
}
}
#[cfg(not(debug_assertions))]
mod debug {
pub fn set_debug(s: &str) {}
#[macro_export]
macro_rules! group {
($($arg:tt)*) => {};
() => {};
}
#[macro_export]
macro_rules! debug_log {
($($arg:tt)*) => {{}};
() => {};
}
#[macro_export]
macro_rules! debug_dbg {
($($val:expr),+ $(,)?) => {};
() => {};
}
#[doc(hidden)]
pub struct GroupGuard;
}
pub use debug::*;
#[cfg(test)]
mod tests {
use crate::{debug_dbg, debug_log, group};
#[test]
fn it_works() {
group!("A Group");
group!("C Group");
{
group!("Sub A Group");
let arr: Vec<_> = (0..3).collect();
debug_dbg!(&arr);
{
group!("Sub Sub A Group");
debug_dbg!(&arr);
}
debug_log!("Hi");
debug_dbg!(&arr);
}
{
group!("B Group");
debug_log!("END");
}
}
}