Skip to main content

peat_protocol/
credentials.rs

1//! # Peat Credentials
2//!
3//! Backend-agnostic credential container used for peer authentication.
4//!
5//! ## Environment Variables
6//!
7//! - `PEAT_APP_ID` — application / formation identifier (required)
8//! - `PEAT_SECRET_KEY` — shared secret key, base64 encoded (optional)
9//!   - `PEAT_SHARED_KEY` is accepted as an alias
10//!
11//! ## Usage
12//!
13//! ```ignore
14//! use peat_protocol::credentials::PeatCredentials;
15//!
16//! let creds = PeatCredentials::from_env()?;
17//! println!("App ID: {}", creds.app_id());
18//! println!("Has secret key: {}", creds.secret_key().is_some());
19//! ```
20
21use std::env;
22
23/// Peat credentials for backend authentication.
24#[derive(Debug, Clone)]
25pub struct PeatCredentials {
26    /// Application / formation identifier (required).
27    app_id: String,
28    /// Shared secret key, base64 encoded (optional).
29    secret_key: Option<String>,
30}
31
32impl PeatCredentials {
33    /// Create credentials from explicit values.
34    pub fn new(app_id: String, secret_key: Option<String>) -> Self {
35        Self { app_id, secret_key }
36    }
37
38    /// Load credentials from environment variables.
39    ///
40    /// Reads `PEAT_APP_ID` (required) and `PEAT_SECRET_KEY` (or
41    /// `PEAT_SHARED_KEY` as an alias, optional).
42    pub fn from_env() -> Result<Self, CredentialsError> {
43        let app_id = env::var("PEAT_APP_ID")
44            .ok()
45            .filter(|v| !v.is_empty())
46            .ok_or(CredentialsError::MissingAppId)?;
47
48        let secret_key = env::var("PEAT_SECRET_KEY")
49            .ok()
50            .filter(|v| !v.is_empty())
51            .or_else(|| env::var("PEAT_SHARED_KEY").ok().filter(|v| !v.is_empty()));
52
53        Ok(Self { app_id, secret_key })
54    }
55
56    /// Load credentials from environment, returning `None` if not configured.
57    pub fn try_from_env() -> Option<Self> {
58        Self::from_env().ok()
59    }
60
61    /// Whether `PEAT_APP_ID` is configured in the environment.
62    pub fn is_configured() -> bool {
63        env::var("PEAT_APP_ID").ok().is_some_and(|v| !v.is_empty())
64    }
65
66    /// Application identifier.
67    pub fn app_id(&self) -> &str {
68        &self.app_id
69    }
70
71    /// Shared secret key, if configured.
72    pub fn secret_key(&self) -> Option<&str> {
73        self.secret_key.as_deref()
74    }
75
76    /// Whether a secret key is configured.
77    pub fn has_secret_key(&self) -> bool {
78        self.secret_key.is_some()
79    }
80
81    /// Get the secret key, or return an error if missing.
82    pub fn require_secret_key(&self) -> Result<&str, CredentialsError> {
83        self.secret_key
84            .as_deref()
85            .ok_or(CredentialsError::MissingSecretKey)
86    }
87}
88
89/// Errors that can occur when loading credentials.
90#[derive(Debug, Clone, thiserror::Error)]
91pub enum CredentialsError {
92    #[error("PEAT_APP_ID not set")]
93    MissingAppId,
94
95    #[error("PEAT_SECRET_KEY not set")]
96    MissingSecretKey,
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_credentials_new() {
105        let creds = PeatCredentials::new("test-app".to_string(), Some("secret".to_string()));
106
107        assert_eq!(creds.app_id(), "test-app");
108        assert_eq!(creds.secret_key(), Some("secret"));
109    }
110
111    #[test]
112    fn test_credentials_without_secret() {
113        let creds = PeatCredentials::new("test-app".to_string(), None);
114
115        assert_eq!(creds.app_id(), "test-app");
116        assert!(creds.secret_key().is_none());
117        assert!(!creds.has_secret_key());
118    }
119
120    #[test]
121    fn test_require_secret_key_present() {
122        let creds = PeatCredentials::new("test-app".to_string(), Some("secret".to_string()));
123
124        assert_eq!(creds.require_secret_key().unwrap(), "secret");
125    }
126
127    #[test]
128    fn test_require_secret_key_missing() {
129        let creds = PeatCredentials::new("test-app".to_string(), None);
130
131        assert!(creds.require_secret_key().is_err());
132    }
133}