Files
adler32
approx
arrayvec
backtrace
backtrace_sys
bin
bitflags
byteorder
cfg_if
color_quant
crossbeam_deque
crossbeam_epoch
crossbeam_utils
deflate
either
failure
failure_derive
gif
image
inflate
initials
jpeg_decoder
lazy_static
libc
lzw
memoffset
nodrop
num_cpus
num_derive
num_integer
num_iter
num_rational
num_traits
ordered_float
png
proc_macro2
quote
rand
rand_core
rayon
rayon_core
rustc_demangle
rusttype
scoped_threadpool
scopeguard
stb_truetype
syn
synstructure
unicode_xid
  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
use std::cell::UnsafeCell;
use std::env;
use std::ffi::OsString;
use std::fmt;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::sync::Mutex;

pub use super::backtrace::Backtrace;

const GENERAL_BACKTRACE: &str = "RUST_BACKTRACE";
const FAILURE_BACKTRACE: &str = "RUST_FAILURE_BACKTRACE";

pub(super) struct InternalBacktrace {
    backtrace: Option<MaybeResolved>,
}

struct MaybeResolved {
    resolved: Mutex<bool>,
    backtrace: UnsafeCell<Backtrace>,
}

unsafe impl Send for MaybeResolved {}
unsafe impl Sync for MaybeResolved {}

impl InternalBacktrace {
    pub(super) fn new() -> InternalBacktrace {
        static ENABLED: AtomicUsize = ATOMIC_USIZE_INIT;

        match ENABLED.load(Ordering::SeqCst) {
            0 => {
                let enabled = is_backtrace_enabled(|var| env::var_os(var));
                ENABLED.store(enabled as usize + 1, Ordering::SeqCst);
                if !enabled {
                    return InternalBacktrace { backtrace: None }
                }
            }
            1 => return InternalBacktrace { backtrace: None },
            _ => {}
        }

        InternalBacktrace {
            backtrace: Some(MaybeResolved {
                resolved: Mutex::new(false),
                backtrace: UnsafeCell::new(Backtrace::new_unresolved()),
            }),
        }
    }

    pub(super) fn none() -> InternalBacktrace {
        InternalBacktrace { backtrace: None }
    }

    pub(super) fn as_backtrace(&self) -> Option<&Backtrace> {
        let bt = match self.backtrace {
            Some(ref bt) => bt,
            None => return None,
        };
        let mut resolved = bt.resolved.lock().unwrap();
        unsafe {
            if !*resolved {
                (*bt.backtrace.get()).resolve();
                *resolved = true;
            }
            Some(&*bt.backtrace.get())
        }
    }

    pub(super) fn is_none(&self) -> bool {
        self.backtrace.is_none()
    }
}

impl fmt::Debug for InternalBacktrace {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("InternalBacktrace")
            .field("backtrace", &self.as_backtrace())
            .finish()
    }
}

fn is_backtrace_enabled<F: Fn(&str) -> Option<OsString>>(get_var: F) -> bool {
    match get_var(FAILURE_BACKTRACE) {
        Some(ref val) if val != "0" => true,
        Some(ref val) if val == "0" => false,
        _ => match get_var(GENERAL_BACKTRACE) {
            Some(ref val) if val != "0" => true,
            _                           => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const YEA: Option<&str> = Some("1");
    const NAY: Option<&str> = Some("0");
    const NOT_SET: Option<&str> = None;

    macro_rules! test_enabled {
        (failure: $failure:ident, general: $general:ident => $result:expr) => {{
            assert_eq!(is_backtrace_enabled(|var| match var {
                FAILURE_BACKTRACE   => $failure.map(OsString::from),
                GENERAL_BACKTRACE   => $general.map(OsString::from),
                _                   => panic!()
            }), $result);
        }}
    }

    #[test]
    fn always_enabled_if_failure_is_set_to_yes() {
        test_enabled!(failure: YEA, general: YEA => true);
        test_enabled!(failure: YEA, general: NOT_SET => true);
        test_enabled!(failure: YEA, general: NAY => true);
    }

    #[test]
    fn never_enabled_if_failure_is_set_to_no() {
        test_enabled!(failure: NAY, general: YEA => false);
        test_enabled!(failure: NAY, general: NOT_SET => false);
        test_enabled!(failure: NAY, general: NAY => false);
    }

    #[test]
    fn follows_general_if_failure_is_not_set() {
        test_enabled!(failure: NOT_SET, general: YEA => true);
        test_enabled!(failure: NOT_SET, general: NOT_SET => false);
        test_enabled!(failure: NOT_SET, general: NAY => false);
    }
}