#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("failed to connect to {server}")]
Connect {
server: String,
#[source]
source: Box<tiberius::error::Error>,
},
#[error("network I/O error")]
Io(#[from] std::io::Error),
#[error("query failed")]
Query(#[source] Box<tiberius::error::Error>),
#[error("column '{0}' not found")]
ColumnNotFound(String),
#[error("column index {index} out of range ({len} columns)")]
ColumnIndexOutOfRange {
index: usize,
len: usize,
},
#[error("expected exactly one row, found {found}")]
UnexpectedRowCount {
found: usize,
},
#[error("cannot convert column '{column}' from {actual} to {target}")]
Conversion {
column: String,
actual: &'static str,
target: &'static str,
},
#[error("invalid decimal from server (value {value}, scale {scale})")]
Decimal {
value: i128,
scale: u8,
},
#[error("invalid configuration: {0}")]
Config(String),
}
impl From<tiberius::error::Error> for Error {
fn from(e: tiberius::error::Error) -> Self {
Error::Query(Box::new(e))
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
fn assert_traits<T: std::error::Error + Send + Sync + 'static>() {}
#[test]
fn error_is_send_sync_static() {
assert_traits::<Error>();
}
#[test]
fn messages_carry_context() {
let e = Error::ColumnNotFound("id".into());
assert_eq!(e.to_string(), "column 'id' not found");
let e = Error::UnexpectedRowCount { found: 3 };
assert_eq!(e.to_string(), "expected exactly one row, found 3");
let e = Error::Conversion {
column: "amount".into(),
actual: "Text",
target: "i32",
};
assert_eq!(
e.to_string(),
"cannot convert column 'amount' from Text to i32"
);
}
}