use std::str::FromStr;
use thiserror::Error;
#[cfg(feature = "bws")]
mod bws;
#[cfg(feature = "infisical")]
mod infisical;
#[cfg(any(feature = "op", feature = "connect"))]
mod op;
#[cfg(feature = "bws")]
pub use bws::BwsReference;
#[cfg(feature = "infisical")]
pub use infisical::{
InfisicalParseError, InfisicalPath, InfisicalProjectId, InfisicalReference,
InfisicalSecretType, InfisicalSlug,
};
#[cfg(any(feature = "op", feature = "connect"))]
pub use op::{OpParseError, OpReference};
#[derive(Debug, Error)]
pub enum ReferenceParseError {
#[error("unknown or invalid secret format: {0}")]
UnknownFormat(String),
#[cfg(any(feature = "op", feature = "connect"))]
#[error(transparent)]
Op(#[from] OpParseError),
#[cfg(feature = "bws")]
#[error("invalid BWS UUID: {0}")]
Bws(#[from] uuid::Error),
#[cfg(feature = "infisical")]
#[error(transparent)]
Infisical(#[from] InfisicalParseError),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SecretReference {
#[cfg(any(feature = "op", feature = "connect"))]
OnePassword(OpReference),
#[cfg(feature = "bws")]
Bws(BwsReference),
#[cfg(feature = "infisical")]
Infisical(InfisicalReference),
#[cfg(any(test, doctest, feature = "testing"))]
Mock(String),
}
pub trait ReferenceSyntax: Sized {
fn try_parse(raw: &str) -> Option<Self>;
}
pub trait ReferenceParser: Send + Sync {
fn parse(&self, raw: &str) -> Option<SecretReference>;
}
pub trait Extract: Sized {
fn extract(r: &SecretReference) -> Option<&Self>;
}
pub trait HasReference {
type Reference: ReferenceSyntax + Into<SecretReference> + Extract;
}
impl<T> ReferenceParser for T
where
T: HasReference + Send + Sync,
{
fn parse(&self, raw: &str) -> Option<SecretReference> {
let parsed = T::Reference::try_parse(raw)?;
Some(parsed.into())
}
}
impl std::fmt::Display for SecretReference {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
#[cfg(any(feature = "op", feature = "connect"))]
Self::OnePassword(reference) => write!(f, "{}", reference),
#[cfg(feature = "bws")]
Self::Bws(reference) => write!(f, "{}", reference),
#[cfg(feature = "infisical")]
Self::Infisical(reference) => write!(f, "{}", reference),
#[cfg(any(test, doctest, feature = "testing"))]
Self::Mock(reference) => write!(f, "{}", reference),
}
}
}
impl FromStr for SecretReference {
type Err = ReferenceParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[cfg(any(feature = "op", feature = "connect"))]
if s.starts_with("op://") {
let op_ref = OpReference::from_str(s)?;
return Ok(Self::OnePassword(op_ref));
}
#[cfg(feature = "infisical")]
if s.starts_with("infisical://") {
let infisical_ref = InfisicalReference::from_str(s)?;
return Ok(Self::Infisical(infisical_ref));
}
#[cfg(feature = "bws")]
if let Ok(bws_ref) = BwsReference::from_str(s) {
return Ok(Self::Bws(bws_ref));
}
Err(ReferenceParseError::UnknownFormat(s.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_invalid() {
assert!(SecretReference::from_str("not-a-secret").is_err());
}
}