use std::env;
use std::fmt;
use crate::types::BugwatchOptions;
pub mod vars {
pub const API_KEY: &str = "BUGWATCH_API_KEY";
pub const ENVIRONMENT: &str = "BUGWATCH_ENVIRONMENT";
pub const RELEASE: &str = "BUGWATCH_RELEASE";
pub const DEBUG: &str = "BUGWATCH_DEBUG";
pub const ENDPOINT: &str = "BUGWATCH_ENDPOINT";
}
#[derive(Debug, Clone)]
pub enum EnvError {
MissingApiKey,
InvalidApiKeyFormat(String),
}
impl fmt::Display for EnvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EnvError::MissingApiKey => write!(
f,
"Bugwatch: API key required. Set {} environment variable or pass options explicitly",
vars::API_KEY
),
EnvError::InvalidApiKeyFormat(key) => {
let preview = if key.len() > 10 {
format!("{}...", &key[..6])
} else {
"[hidden]".to_string()
};
write!(
f,
"Bugwatch: API key should start with 'bw_'. Got '{}'",
preview
)
}
}
}
}
impl std::error::Error for EnvError {}
pub fn get_env_options() -> Result<BugwatchOptions, EnvError> {
let api_key = env::var(vars::API_KEY).map_err(|_| EnvError::MissingApiKey)?;
if !api_key.starts_with("bw_") {
let preview = if api_key.len() > 10 {
format!("{}...", &api_key[..6])
} else {
"[hidden]".to_string()
};
tracing::warn!(
"Bugwatch: API key should start with 'bw_'. Got '{}'",
preview
);
}
let mut options = BugwatchOptions::new(api_key);
if let Ok(environment) = env::var(vars::ENVIRONMENT) {
options = options.with_environment(environment);
}
if let Ok(release) = env::var(vars::RELEASE) {
options = options.with_release(release);
}
if env::var(vars::DEBUG).ok().as_deref() == Some("true") {
options = options.with_debug(true);
}
if let Ok(endpoint) = env::var(vars::ENDPOINT) {
options = options.with_endpoint(endpoint);
}
Ok(options)
}
pub fn has_api_key() -> bool {
env::var(vars::API_KEY).is_ok()
}
pub fn validate_api_key(key: &str) -> Result<(), EnvError> {
if !key.starts_with("bw_") {
return Err(EnvError::InvalidApiKeyFormat(key.to_string()));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn test_has_api_key() {
let original = env::var(vars::API_KEY).ok();
env::remove_var(vars::API_KEY);
assert!(!has_api_key());
env::set_var(vars::API_KEY, "bw_test_key");
assert!(has_api_key());
match original {
Some(val) => env::set_var(vars::API_KEY, val),
None => env::remove_var(vars::API_KEY),
}
}
#[test]
fn test_validate_api_key() {
assert!(validate_api_key("bw_test_key").is_ok());
assert!(validate_api_key("bw_live_xxxxx").is_ok());
assert!(validate_api_key("invalid").is_err());
assert!(validate_api_key("").is_err());
}
#[test]
fn test_env_error_display() {
let err = EnvError::MissingApiKey;
assert!(err.to_string().contains("BUGWATCH_API_KEY"));
let err = EnvError::InvalidApiKeyFormat("short".to_string());
assert!(err.to_string().contains("[hidden]"));
assert!(err.to_string().contains("bw_"));
let err = EnvError::InvalidApiKeyFormat("very_long_invalid_key_here".to_string());
assert!(err.to_string().contains("very_l..."));
}
}