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}
32
33impl<'buf> From<ErrPayloadBytes<'buf>> for Error {
34    fn from(value: ErrPayloadBytes) -> Self {
35        match ErrPayload::try_from(value) {
36            Ok(err_payload) => Error::ServerError(err_payload),
37            Err(err) => err,
38        }
39    }
40}
41
42impl From<core::convert::Infallible> for Error {
43    fn from(err: core::convert::Infallible) -> Self {
44        match err {}
45    }
46}
47
48impl Error {
49    pub fn from_debug(err: impl std::fmt::Debug) -> Self {
50        Self::LibraryBug(color_eyre::eyre::eyre!(format!("{:#?}", err)))
51    }
52
53    /// Returns true if the error indicates the connection is broken and cannot be reused.
54    ///
55    /// This is conservative - returns true (broken) when in doubt.
56    pub fn is_conn_broken(&self) -> bool {
57        match self {
58            Error::ServerError(err_payload) => {
59                match err_payload.sql_state.as_str() {
60                    // Integrity errors - connection still usable
61                    "23000" => false,
62                    // Data errors - connection still usable
63                    "22001" | "22003" | "22007" | "22012" => false,
64                    // Programming errors - connection still usable
65                    "42000" | "42S02" | "42S22" => false,
66                    // Not supported - connection still usable
67                    "0A000" => false,
68                    // Everything else - assume broken
69                    _ => true,
70                }
71            }
72            // All other errors - assume broken
73            _ => true,
74        }
75    }
76}
77
78impl<Src, Dst: ?Sized> From<zerocopy::CastError<Src, Dst>> for Error {
79    fn from(err: zerocopy::CastError<Src, Dst>) -> Self {
80        Self::LibraryBug(color_eyre::eyre::eyre!("{:#?}", err))
81    }
82}