use boson_core::{redact_credentials_in_text, redact_endpoint, BosonError};
#[derive(Debug, thiserror::Error)]
#[error("{0}")]
struct SqlBackendCause(String);
pub fn map_err(e: &sqlx::Error) -> BosonError {
let detail = redact_credentials_in_text(&e.to_string());
let message = format!("sql backend: {detail}");
BosonError::backend_source(message, SqlBackendCause(detail))
}
pub fn map_connect_err(kind: &str, url: &str, e: &sqlx::Error) -> BosonError {
let detail = redact_credentials_in_text(&e.to_string());
let message = format!("{kind} connect {}: {detail}", redact_endpoint(url));
BosonError::backend_source(message, SqlBackendCause(detail))
}
#[cfg(test)]
mod tests {
use std::error::Error as StdError;
use super::{map_connect_err, map_err};
#[test]
fn map_err_redacts_embedded_userinfo() {
let msg = "error communicating with database: postgres://u:secret@localhost/db";
let redacted = boson_core::redact_credentials_in_text(msg);
assert!(redacted.contains("postgres://***@localhost/db"));
assert!(!redacted.contains("secret"));
let mapped = map_err(&sqlx::Error::PoolTimedOut);
assert!(mapped.to_string().contains("sql backend"));
assert!(mapped.to_string().contains("timed out") || mapped.to_string().contains("Timeout"));
assert!(mapped.source().is_some());
}
#[test]
fn map_connect_err_redacts_userinfo() {
let mapped = map_connect_err(
"postgres",
"postgres://user:secret@host/db",
&sqlx::Error::PoolTimedOut,
);
let msg = mapped.to_string();
assert!(msg.contains("postgres://***@host/db"), "msg: {msg}");
assert!(!msg.contains("secret"), "msg: {msg}");
assert!(mapped.source().is_some());
}
}