zero_mysql/
error.rs

1use thiserror::Error;
2
3pub use color_eyre::eyre::eyre;
4
5use crate::protocol::{response::ErrPayload, response::ErrPayloadBytes};
6
7pub type Result<T> = core::result::Result<T, Error>;
8
9#[derive(Debug, Error)]
10pub enum Error {
11    // ─── Server Error ────────────────────────────────────────────────────
12    #[error("Server Error: {0}")]
13    ServerError(#[from] ErrPayload),
14    // ─── Incorrect Usage ─────────────────────────────────────────────────
15    #[error(
16        "Connection mismatch: transaction started on connection {expected}, but commit/rollback called on connection {actual}"
17    )]
18    ConnectionMismatch { expected: u64, actual: u64 },
19    #[error("Bad usage error: {0}")]
20    BadUsageError(String),
21    // ─── Temporary Error ─────────────────────────────────────────────────
22    #[error("IO error: {0}")]
23    IoError(#[from] std::io::Error),
24    // ─── Library Error ───────────────────────────────────────────────────
25    #[error("A bug in zero-mysql: {0}")]
26    LibraryBug(#[from] color_eyre::Report),
27    #[error("Unsupported authentication plugin: {0}")]
28    Unsupported(String),
29    #[error("Cannot nest transactions - a transaction is already active")]
30    NestedTransaction,
31    #[error("Missing column: {0}")]
32    MissingColumn(&'static str),
33    #[error("Unknown column: {0}")]
34    UnknownColumn(String),
35}
36
37impl<'buf> From<ErrPayloadBytes<'buf>> for Error {
38    fn from(value: ErrPayloadBytes) -> Self {
39        match ErrPayload::try_from(value) {
40            Ok(err_payload) => Error::ServerError(err_payload),
41            Err(err) => err,
42        }
43    }
44}
45
46impl From<core::convert::Infallible> for Error {
47    fn from(err: core::convert::Infallible) -> Self {
48        match err {}
49    }
50}
51
52impl Error {
53    pub fn from_debug(err: impl std::fmt::Debug) -> Self {
54        Self::LibraryBug(color_eyre::eyre::eyre!(format!("{:#?}", err)))
55    }
56
57    /// Returns true if the error indicates the connection is broken and cannot be reused.
58    ///
59    /// This is conservative - returns true (broken) when in doubt.
60    pub fn is_conn_broken(&self) -> bool {
61        match self {
62            Error::ServerError(err_payload) => {
63                match err_payload.sql_state.as_str() {
64                    // Integrity errors - connection still usable
65                    "23000" => false,
66                    // Data errors - connection still usable
67                    "22001" | "22003" | "22007" | "22012" => false,
68                    // Programming errors - connection still usable
69                    "42000" | "42S02" | "42S22" => false,
70                    // Not supported - connection still usable
71                    "0A000" => false,
72                    // Everything else - assume broken
73                    _ => true,
74                }
75            }
76            // User errors - connection still usable
77            Error::BadUsageError(_) | Error::MissingColumn(_) | Error::UnknownColumn(_) => false,
78            // All other errors - assume broken
79            _ => true,
80        }
81    }
82}
83
84impl<Src, Dst: ?Sized> From<zerocopy::CastError<Src, Dst>> for Error {
85    fn from(err: zerocopy::CastError<Src, Dst>) -> Self {
86        Self::LibraryBug(color_eyre::eyre::eyre!("{:#?}", err))
87    }
88}