use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SecretSourceKind {
Store,
Env,
File,
}
impl SecretSourceKind {
pub fn as_str(self) -> &'static str {
match self {
Self::Store => "store",
Self::Env => "env",
Self::File => "file",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SecretRef {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub from_store: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub from_env: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub from_file: Option<PathBuf>,
}
impl SecretRef {
pub fn source_kind(&self) -> Option<SecretSourceKind> {
match (
self.from_store.is_some(),
self.from_env.is_some(),
self.from_file.is_some(),
) {
(true, false, false) => Some(SecretSourceKind::Store),
(false, true, false) => Some(SecretSourceKind::Env),
(false, false, true) => Some(SecretSourceKind::File),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn source_kind_reports_variant() {
assert_eq!(
SecretRef {
from_store: Some("x".into()),
from_env: None,
from_file: None,
}
.source_kind(),
Some(SecretSourceKind::Store)
);
assert_eq!(
SecretRef {
from_store: None,
from_env: Some("Y".into()),
from_file: None,
}
.source_kind(),
Some(SecretSourceKind::Env)
);
assert_eq!(
SecretRef {
from_store: None,
from_env: None,
from_file: Some(PathBuf::from("/z")),
}
.source_kind(),
Some(SecretSourceKind::File)
);
let empty = SecretRef {
from_store: None,
from_env: None,
from_file: None,
};
assert_eq!(empty.source_kind(), None);
}
#[test]
fn deny_unknown_fields() {
let bad = r#"{ "from_stor": "typo" }"#;
let res: Result<SecretRef, _> = serde_json::from_str(bad);
assert!(res.is_err());
}
#[test]
fn roundtrip_json() {
let r = SecretRef {
from_store: Some("API_KEY".into()),
from_env: None,
from_file: None,
};
let json = serde_json::to_string(&r).unwrap();
let back: SecretRef = serde_json::from_str(&json).unwrap();
assert_eq!(r, back);
}
#[test]
fn serialization_omits_empty_fields() {
let r = SecretRef {
from_store: Some("X".into()),
from_env: None,
from_file: None,
};
let json = serde_json::to_string(&r).unwrap();
assert!(json.contains("from_store"));
assert!(!json.contains("from_env"));
assert!(!json.contains("from_file"));
}
}