use crate::runtime::redaction::redact_text;
use std::error::Error;
use std::fmt;
pub type SddResult<T> = std::result::Result<T, SddError>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum SddError {
Config { message: String },
Runtime { message: String },
Provider { message: String },
}
impl SddError {
pub fn config(message: impl Into<String>) -> Self {
Self::Config {
message: message.into(),
}
}
pub fn runtime(message: impl Into<String>) -> Self {
Self::Runtime {
message: message.into(),
}
}
pub fn provider(message: impl Into<String>) -> Self {
Self::Provider {
message: message.into(),
}
}
fn kind(&self) -> &'static str {
match self {
Self::Config { .. } => "config",
Self::Runtime { .. } => "runtime",
Self::Provider { .. } => "provider",
}
}
fn message(&self) -> &str {
match self {
Self::Config { message } | Self::Runtime { message } | Self::Provider { message } => {
message
}
}
}
}
impl fmt::Display for SddError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"sdd {} error: {}",
self.kind(),
redact_text(self.message())
)
}
}
impl Error for SddError {}
#[cfg(test)]
mod tests {
use super::*;
use crate::runtime::redaction::Secret;
#[test]
fn display_redacts_secret_values_embedded_in_errors() {
let secret = Secret::new("sk-test-secret");
let error = SddError::provider(format!("provider auth failed with {secret}"));
let rendered = error.to_string();
assert!(rendered.contains("[REDACTED]"));
assert!(!rendered.contains("sk-test-secret"));
}
#[test]
fn result_alias_uses_sdd_error() {
let result: SddResult<()> = Err(SddError::runtime("OPENAI_API_KEY=sk-test-secret"));
let rendered = match result {
Ok(()) => unreachable!("esperado Err do alias SddResult"),
Err(error) => error.to_string(),
};
assert!(rendered.contains("OPENAI_API_KEY=[REDACTED]"));
assert!(!rendered.contains("sk-test-secret"));
}
}