use std::{fmt, path::PathBuf};
use crate::error::Error;
pub const PACK_ROOT: &str = "PACK_ROOT";
pub const CF_API_KEY: &str = "CF_API_KEY";
#[derive(Clone)]
pub struct Secret(String);
impl Secret {
pub fn expose(&self) -> &str {
&self.0
}
}
impl fmt::Debug for Secret {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Secret(<redacted>)")
}
}
impl fmt::Display for Secret {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<redacted>")
}
}
pub fn load_dotenv() {
match dotenvy::dotenv() {
Ok(path) => log::debug!("loaded environment from \"{}\"", path.display()),
Err(err) if err.not_found() => {}
Err(err) => log::warn!("could not read .env: {err}"),
}
}
pub fn pack_root() -> PathBuf {
match std::env::var(PACK_ROOT) {
Ok(value) if !value.trim().is_empty() => PathBuf::from(value),
_ => PathBuf::from("."),
}
}
pub fn curseforge_api_key() -> Result<Secret, Error> {
match std::env::var(CF_API_KEY) {
Ok(value) if !value.trim().is_empty() => Ok(Secret(value)),
_ => Err(Error::MissingEnv(CF_API_KEY)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_secret_redacts_itself_in_both_formats() {
let secret = Secret("hunter2".to_owned());
assert_eq!(format!("{secret}"), "<redacted>");
assert_eq!(format!("{secret:?}"), "Secret(<redacted>)");
assert_eq!(secret.expose(), "hunter2");
}
}