Skip to main content

lnc_client/
error.rs

1use std::fmt;
2
3use std::net::SocketAddr;
4
5/// Errors that can occur during client operations
6#[derive(Debug)]
7pub enum ClientError {
8    /// Failed to establish a connection to the server
9    ConnectionFailed(std::io::Error),
10    /// Connection was closed by the server
11    ConnectionClosed,
12    /// I/O error during communication
13    IoError(std::io::Error),
14    /// Protocol-level error (malformed data, invalid state)
15    ProtocolError(String),
16    /// Received an unexpected or invalid response from the server
17    InvalidResponse(String),
18    /// Operation timed out
19    Timeout,
20    /// CRC checksum mismatch indicating data corruption
21    CrcMismatch {
22        /// Expected CRC value
23        expected: u32,
24        /// Actual CRC value received
25        actual: u32,
26    },
27    /// Server is applying backpressure, client should slow down
28    ServerBackpressure,
29    /// Server returned an error message
30    ServerError(String),
31    /// Server is not the leader, redirect to the specified address
32    NotLeader {
33        /// Address of the current leader, if known
34        leader_addr: Option<SocketAddr>,
35    },
36    /// TLS handshake or configuration error
37    TlsError(String),
38}
39
40impl fmt::Display for ClientError {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            Self::ConnectionFailed(e) => write!(f, "Connection failed: {}", e),
44            Self::ConnectionClosed => write!(f, "Connection closed by server"),
45            Self::IoError(e) => write!(f, "I/O error: {}", e),
46            Self::ProtocolError(msg) => write!(f, "Protocol error: {}", msg),
47            Self::InvalidResponse(msg) => write!(f, "Invalid response: {}", msg),
48            Self::Timeout => write!(f, "Operation timed out"),
49            Self::CrcMismatch { expected, actual } => {
50                write!(
51                    f,
52                    "CRC mismatch: expected {:#x}, got {:#x}",
53                    expected, actual
54                )
55            },
56            Self::ServerBackpressure => write!(f, "Server signaled backpressure"),
57            Self::ServerError(msg) => write!(f, "Server error: {}", msg),
58            Self::NotLeader { leader_addr } => match leader_addr {
59                Some(addr) => write!(f, "Not leader, redirect to {}", addr),
60                None => write!(f, "Not leader, leader unknown"),
61            },
62            Self::TlsError(msg) => write!(f, "TLS error: {}", msg),
63        }
64    }
65}
66
67/// Parse a NOT_LEADER error message and extract the redirect address if present
68pub fn parse_not_leader_error(msg: &str) -> Option<Option<SocketAddr>> {
69    if !msg.starts_with("NOT_LEADER:") {
70        return None;
71    }
72
73    if msg.contains("leader unknown") {
74        return Some(None);
75    }
76
77    // Parse "NOT_LEADER: redirect to X.X.X.X:PORT"
78    if let Some(addr_str) = msg.strip_prefix("NOT_LEADER: redirect to ") {
79        if let Ok(addr) = addr_str.trim().parse::<SocketAddr>() {
80            return Some(Some(addr));
81        }
82    }
83
84    Some(None)
85}
86
87impl std::error::Error for ClientError {
88    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
89        match self {
90            Self::ConnectionFailed(e) | Self::IoError(e) => Some(e),
91            _ => None,
92        }
93    }
94}
95
96impl From<std::io::Error> for ClientError {
97    fn from(err: std::io::Error) -> Self {
98        Self::IoError(err)
99    }
100}
101
102impl From<lnc_core::LanceError> for ClientError {
103    fn from(err: lnc_core::LanceError) -> Self {
104        Self::ProtocolError(err.to_string())
105    }
106}
107
108pub type Result<T> = std::result::Result<T, ClientError>;