use secrecy::{ExposeSecret, SecretString};
use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize)]
pub struct Account {
pub id: String,
#[serde(
serialize_with = "serialize_token",
deserialize_with = "deserialize_token"
)]
token: SecretString,
}
impl Account {
pub fn new(id: impl Into<String>, token: impl Into<String>) -> Self {
Self {
id: id.into(),
token: SecretString::new(token.into().into()),
}
}
pub fn from_env() -> crate::Result<Self> {
let id = std::env::var("CF_ACCOUNT_ID")
.map_err(|_| crate::FlarerError::Config("CF_ACCOUNT_ID not set".into()))?;
let token = std::env::var("CF_API_TOKEN")
.map_err(|_| crate::FlarerError::Config("CF_API_TOKEN not set".into()))?;
if id.is_empty() || token.is_empty() {
return Err(crate::FlarerError::Config(
"CF_ACCOUNT_ID / CF_API_TOKEN must not be empty".into(),
));
}
Ok(Self::new(id, token))
}
pub fn token(&self) -> &str {
self.token.expose_secret()
}
}
impl std::fmt::Debug for Account {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Account")
.field("id", &self.id)
.field("token", &"<redacted>")
.finish()
}
}
fn serialize_token<S: serde::Serializer>(
s: &SecretString,
ser: S,
) -> std::result::Result<S::Ok, S::Error> {
ser.serialize_str(s.expose_secret())
}
fn deserialize_token<'de, D: serde::Deserializer<'de>>(
de: D,
) -> std::result::Result<SecretString, D::Error> {
let raw = String::deserialize(de)?;
Ok(SecretString::new(raw.into()))
}