1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#![feature(crate_visibility_modifier)]
#![feature(macro_vis_matcher)]

use std::cell::RefCell;

#[macro_use]
extern crate lazy_static;

#[macro_use]
mod index;

lazy_static! {
    pub static ref DEBUG_ENABLED: bool = {
        use std::env;
        env::var("CHALK_DEBUG")
            .ok()
            .and_then(|s| s.parse::<u32>().ok())
            .map(|x| x >= 2)
            .unwrap_or(false)
    };

    pub static ref INFO_ENABLED: bool = {
        use std::env;
        env::var("CHALK_DEBUG")
            .ok()
            .and_then(|s| s.parse::<u32>().ok())
            .map(|x| x >= 1)
            .unwrap_or(false)
    };
}

thread_local! {
    crate static INDENT: RefCell<Vec<String>> = RefCell::new(vec![]);
}

// When CHALK_DEBUG is enabled, we only allow this many frames of
// nested processing, at which point we assume something has gone
// awry.
const OVERFLOW_DEPTH: usize = 100;

#[macro_export]
macro_rules! debug {
    ($($t:tt)*) => {
        if *$crate::DEBUG_ENABLED {
            $crate::dump(&format!($($t)*), "");
        }
    }
}

#[macro_export]
macro_rules! debug_heading {
    ($($t:tt)*) => {
        let _ = &if *$crate::DEBUG_ENABLED {
            let string = format!($($t)*);
            $crate::dump(&string, " {");
            $crate::Indent::new(true, string)
        } else {
            $crate::Indent::new(false, String::new())
        };
    }
}

#[macro_export]
macro_rules! info {
    ($($t:tt)*) => {
        if *$crate::INFO_ENABLED {
            $crate::dump(&format!($($t)*), "");
        }
    }
}

#[macro_export]
macro_rules! info_heading {
    ($($t:tt)*) => {
        let _ = &if *$crate::INFO_ENABLED {
            let string = format!($($t)*);
            $crate::dump(&string, " {");
            $crate::Indent::new(true, string)
        } else {
            $crate::Indent::new(false, String::new())
        };
    }
}

pub fn dump(string: &str, suffix: &str) {
    let indent = INDENT.with(|i| i.borrow().len());
    let mut first = true;
    for line in string.lines() {
        if first {
            for _ in 0..indent {
                eprint!(": ");
            }
            eprint!("| ");
        } else {
            eprintln!();
            for _ in 0..indent {
                eprint!(": ");
            }
            eprint!(": ");
        }
        eprint!("{}", line);
        first = false;
    }

    eprintln!("{}", suffix);
}

pub struct Indent {
    enabled: bool,
}

impl Indent {
    pub fn new(enabled: bool, value: String) -> Self {
        if enabled {
            INDENT.with(|i| {
                i.borrow_mut().push(value);
                if i.borrow().len() > OVERFLOW_DEPTH {
                    eprintln!("CHALK_DEBUG OVERFLOW:");
                    for v in i.borrow().iter().rev() {
                        eprintln!("- {}", v);
                    }
                    panic!("CHALK_DEBUG OVERFLOW")
                }
            });
        }
        Indent { enabled }
    }
}

impl Drop for Indent {
    fn drop(&mut self) {
        if self.enabled {
            INDENT.with(|i| i.borrow_mut().pop().unwrap());
            dump("}", "");
        }
    }
}