use std::path::Path;
use super::config::read_section;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConsentState {
Enabled,
Disabled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecidedBy {
SentryDisabledEnv,
NoBakedDsn,
ConfigFile,
DefaultEnabled,
}
#[derive(Debug, Clone, Copy)]
pub struct Resolved {
pub state: ConsentState,
pub decided_by: DecidedBy,
}
impl Resolved {
pub fn enabled(&self) -> bool {
self.state == ConsentState::Enabled
}
}
pub fn resolve(config_path: &Path, dsn_present: bool) -> Resolved {
if is_truthy_env("SENTRY_DISABLED") {
return Resolved {
state: ConsentState::Disabled,
decided_by: DecidedBy::SentryDisabledEnv,
};
}
if !dsn_present {
return Resolved {
state: ConsentState::Disabled,
decided_by: DecidedBy::NoBakedDsn,
};
}
match read_section(config_path) {
Ok(Some(section)) if !section.enabled => Resolved {
state: ConsentState::Disabled,
decided_by: DecidedBy::ConfigFile,
},
Ok(Some(_)) | Ok(None) | Err(_) => Resolved {
state: ConsentState::Enabled,
decided_by: if matches!(read_section(config_path), Ok(Some(_))) {
DecidedBy::ConfigFile
} else {
DecidedBy::DefaultEnabled
},
},
}
}
fn is_truthy_env(name: &str) -> bool {
match std::env::var(name) {
Ok(v) => {
let v = v.trim().to_ascii_lowercase();
!matches!(v.as_str(), "" | "0" | "false" | "no" | "off")
}
Err(_) => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
use tempfile::TempDir;
static ENV_LOCK: Mutex<()> = Mutex::new(());
fn with_clean_env<F: FnOnce()>(f: F) {
let _g = ENV_LOCK.lock().unwrap_or_else(|p| p.into_inner());
unsafe {
std::env::remove_var("SENTRY_DISABLED");
}
f();
}
#[test]
fn test_resolve_disabled_when_env_set() {
with_clean_env(|| {
unsafe {
std::env::set_var("SENTRY_DISABLED", "1");
}
let tmp = TempDir::new().unwrap();
let r = resolve(&tmp.path().join("config.toml"), true);
assert_eq!(r.state, ConsentState::Disabled);
assert_eq!(r.decided_by, DecidedBy::SentryDisabledEnv);
unsafe {
std::env::remove_var("SENTRY_DISABLED");
}
});
}
#[test]
fn test_resolve_disabled_when_no_dsn() {
with_clean_env(|| {
let tmp = TempDir::new().unwrap();
let r = resolve(&tmp.path().join("config.toml"), false);
assert_eq!(r.state, ConsentState::Disabled);
assert_eq!(r.decided_by, DecidedBy::NoBakedDsn);
});
}
#[test]
fn test_resolve_disabled_when_config_says_false() {
with_clean_env(|| {
let tmp = TempDir::new().unwrap();
let p = tmp.path().join("config.toml");
std::fs::write(&p, "[crashreport]\nenabled = false\n").unwrap();
let r = resolve(&p, true);
assert_eq!(r.state, ConsentState::Disabled);
assert_eq!(r.decided_by, DecidedBy::ConfigFile);
});
}
#[test]
fn test_resolve_enabled_by_default_when_config_missing() {
with_clean_env(|| {
let tmp = TempDir::new().unwrap();
let r = resolve(&tmp.path().join("config.toml"), true);
assert_eq!(r.state, ConsentState::Enabled);
assert_eq!(r.decided_by, DecidedBy::DefaultEnabled);
});
}
#[test]
fn test_resolve_enabled_when_config_says_true() {
with_clean_env(|| {
let tmp = TempDir::new().unwrap();
let p = tmp.path().join("config.toml");
std::fs::write(&p, "[crashreport]\nenabled = true\n").unwrap();
let r = resolve(&p, true);
assert_eq!(r.state, ConsentState::Enabled);
assert_eq!(r.decided_by, DecidedBy::ConfigFile);
});
}
}