use super::SecretError;
const PREFIX: &str = "vault:";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VaultRef {
pub mount: String,
pub path: String,
pub field: String,
}
impl std::fmt::Display for VaultRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{PREFIX}{}/{}#{}", self.mount, self.path, self.field)
}
}
pub fn parse_reference(raw: &str) -> Result<Option<VaultRef>, SecretError> {
let Some(rest) = raw.strip_prefix(PREFIX) else {
return Ok(None);
};
let invalid =
|| SecretError::InvalidReference("expected vault:<mount>/<path>#<field>".to_string());
let (location, field) = rest.split_once('#').ok_or_else(invalid)?;
let (mount, path) = location.split_once('/').ok_or_else(invalid)?;
if mount.is_empty() || path.is_empty() || field.is_empty() {
return Err(invalid());
}
Ok(Some(VaultRef {
mount: mount.to_string(),
path: path.to_string(),
field: field.to_string(),
}))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn literal_values_are_not_references() -> Result<(), SecretError> {
assert_eq!(parse_reference("sk-abc")?, None);
Ok(())
}
#[test]
fn full_reference_is_parsed() -> Result<(), SecretError> {
let parsed = parse_reference("vault:secret/overbrainer/nanogpt#api_key")?;
assert_eq!(
parsed,
Some(VaultRef {
mount: "secret".into(),
path: "overbrainer/nanogpt".into(),
field: "api_key".into(),
})
);
Ok(())
}
#[test]
fn malformed_references_are_rejected() {
for raw in [
"vault:",
"vault:secret#k",
"vault:secret/path",
"vault:/path#k",
"vault:secret/#k",
"vault:secret/p#",
] {
assert!(
matches!(parse_reference(raw), Err(SecretError::InvalidReference(_))),
"{raw}"
);
}
}
}