Skip to main content

lnc_client/
error.rs

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