use secrecy::{ExposeSecret, SecretString};
use std::sync::Arc;
#[derive(Clone)]
pub struct Credentials {
pub api_key: String,
api_secret: SecretString,
}
impl Credentials {
pub fn new(api_key: impl Into<String>, api_secret: impl Into<String>) -> Self {
Self {
api_key: api_key.into(),
api_secret: SecretString::from(api_secret.into()),
}
}
pub fn expose_secret(&self) -> &str {
self.api_secret.expose_secret()
}
}
impl std::fmt::Debug for Credentials {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Credentials")
.field("api_key", &self.api_key)
.field("api_secret", &"[REDACTED]")
.finish()
}
}
pub trait CredentialsProvider: Send + Sync {
fn get_credentials(&self) -> &Credentials;
}
#[derive(Clone)]
pub struct StaticCredentials {
credentials: Credentials,
}
impl StaticCredentials {
pub fn new(api_key: impl Into<String>, api_secret: impl Into<String>) -> Self {
Self {
credentials: Credentials::new(api_key, api_secret),
}
}
}
impl CredentialsProvider for StaticCredentials {
fn get_credentials(&self) -> &Credentials {
&self.credentials
}
}
impl CredentialsProvider for Arc<StaticCredentials> {
fn get_credentials(&self) -> &Credentials {
&self.credentials
}
}
pub struct EnvCredentials {
credentials: Credentials,
}
impl EnvCredentials {
pub fn from_env() -> Self {
Self::from_env_vars("KRAKEN_API_KEY", "KRAKEN_API_SECRET")
}
pub fn from_env_vars(key_var: &str, secret_var: &str) -> Self {
let api_key = std::env::var(key_var)
.unwrap_or_else(|_| panic!("Environment variable {key_var} not set"));
let api_secret = std::env::var(secret_var)
.unwrap_or_else(|_| panic!("Environment variable {secret_var} not set"));
Self {
credentials: Credentials::new(api_key, api_secret),
}
}
pub fn try_from_env() -> Option<Self> {
Self::try_from_env_vars("KRAKEN_API_KEY", "KRAKEN_API_SECRET")
}
pub fn try_from_env_vars(key_var: &str, secret_var: &str) -> Option<Self> {
let api_key = std::env::var(key_var).ok()?;
let api_secret = std::env::var(secret_var).ok()?;
Some(Self {
credentials: Credentials::new(api_key, api_secret),
})
}
}
impl CredentialsProvider for EnvCredentials {
fn get_credentials(&self) -> &Credentials {
&self.credentials
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_credentials_debug_redacted() {
let creds = Credentials::new("my_key", "super_secret");
let debug_str = format!("{:?}", creds);
assert!(debug_str.contains("my_key"));
assert!(!debug_str.contains("super_secret"));
assert!(debug_str.contains("[REDACTED]"));
}
#[test]
fn test_static_credentials() {
let provider = StaticCredentials::new("key", "secret");
let creds = provider.get_credentials();
assert_eq!(creds.api_key, "key");
assert_eq!(creds.expose_secret(), "secret");
}
}