use std::fmt;
use crate::executor::Family;
#[derive(Debug)]
#[non_exhaustive]
pub enum ExecError {
Build(keelson_core::Error),
Decode {
column: String,
source: keelson_core::Error,
},
MissingColumn {
column: String,
available: Vec<String>,
},
RowNotFound,
TooManyRows,
UnsupportedValue {
type_name: &'static str,
family: Family,
},
Driver(Box<dyn std::error::Error + Send + Sync>),
Other(String),
}
impl ExecError {
pub fn driver(e: impl std::error::Error + Send + Sync + 'static) -> Self {
ExecError::Driver(Box::new(e))
}
pub fn other(msg: impl Into<String>) -> Self {
ExecError::Other(msg.into())
}
}
impl From<keelson_core::Error> for ExecError {
fn from(e: keelson_core::Error) -> Self {
ExecError::Build(e)
}
}
impl fmt::Display for ExecError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExecError::Build(e) => write!(f, "query failed to build: {e}"),
ExecError::Decode { column, source } => write!(f, "column \"{column}\": {source}"),
ExecError::MissingColumn { column, available } => write!(
f,
"no column \"{column}\" in result set (columns: {})",
available.join(", ")
),
ExecError::RowNotFound => {
f.write_str("no rows returned where exactly one was expected")
}
ExecError::TooManyRows => {
f.write_str("more than one row returned where at most one was expected")
}
ExecError::UnsupportedValue { type_name, family } => {
write!(f, "cannot bind a {type_name} value on {family}")
}
ExecError::Driver(e) => write!(f, "driver error: {e}"),
ExecError::Other(msg) => f.write_str(msg),
}
}
}
impl std::error::Error for ExecError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ExecError::Build(e) | ExecError::Decode { source: e, .. } => Some(e),
ExecError::Driver(e) => Some(e.as_ref()),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_errors_name_the_column() {
let e = ExecError::Decode {
column: "email".into(),
source: keelson_core::Error::type_mismatch("String", "NULL"),
};
assert_eq!(
e.to_string(),
"column \"email\": cannot read NULL as String"
);
}
#[test]
fn missing_column_lists_what_was_there() {
let e = ExecError::MissingColumn {
column: "emial".into(),
available: vec!["id".into(), "name".into(), "email".into()],
};
assert_eq!(
e.to_string(),
"no column \"emial\" in result set (columns: id, name, email)"
);
}
#[test]
fn is_a_std_error_with_a_source() {
let e = ExecError::Build(keelson_core::Error::Incomplete("a table"));
assert!(std::error::Error::source(&e).is_some());
}
}