redis_oxide/core/
error.rs1use std::io;
4use thiserror::Error;
5
6pub type RedisResult<T> = Result<T, RedisError>;
8
9#[derive(Error, Debug)]
11pub enum RedisError {
12 #[error("IO error: {0}")]
14 Io(#[from] io::Error),
15
16 #[error("Protocol error: {0}")]
18 Protocol(String),
19
20 #[error("Server error: {0}")]
22 Server(String),
23
24 #[error("MOVED redirect: slot {slot} to {host}:{port}")]
26 Moved {
27 slot: u16,
29 host: String,
31 port: u16,
33 },
34
35 #[error("ASK redirect: slot {slot} to {host}:{port}")]
37 Ask {
38 slot: u16,
40 host: String,
42 port: u16,
44 },
45
46 #[error("Connection error: {0}")]
48 Connection(String),
49
50 #[error("Operation timed out")]
52 Timeout,
53
54 #[error("Type conversion error: {0}")]
56 Type(String),
57
58 #[error("Invalid configuration: {0}")]
60 Config(String),
61
62 #[error("Cluster error: {0}")]
64 Cluster(String),
65
66 #[error("Authentication failed: {0}")]
68 Auth(String),
69
70 #[error("Pool error: {0}")]
72 Pool(String),
73
74 #[error("Maximum retry attempts ({0}) exceeded")]
76 MaxRetriesExceeded(usize),
77
78 #[error("Unexpected response: {0}")]
80 UnexpectedResponse(String),
81}
82
83impl RedisError {
84 #[must_use]
86 pub fn parse_redirect(msg: &str) -> Option<Self> {
87 if let Some(moved_str) = msg.strip_prefix("MOVED ") {
88 let parts: Vec<&str> = moved_str.split_whitespace().collect();
89 if parts.len() == 2 {
90 if let Ok(slot) = parts[0].parse::<u16>() {
91 if let Some((host, port)) = parts[1].rsplit_once(':') {
92 if let Ok(port) = port.parse::<u16>() {
93 return Some(Self::Moved {
94 slot,
95 host: host.to_string(),
96 port,
97 });
98 }
99 }
100 }
101 }
102 }
103
104 if let Some(ask_str) = msg.strip_prefix("ASK ") {
105 let parts: Vec<&str> = ask_str.split_whitespace().collect();
106 if parts.len() == 2 {
107 if let Ok(slot) = parts[0].parse::<u16>() {
108 if let Some((host, port)) = parts[1].rsplit_once(':') {
109 if let Ok(port) = port.parse::<u16>() {
110 return Some(Self::Ask {
111 slot,
112 host: host.to_string(),
113 port,
114 });
115 }
116 }
117 }
118 }
119 }
120
121 None
122 }
123
124 #[must_use]
126 pub const fn is_redirect(&self) -> bool {
127 matches!(self, Self::Moved { .. } | Self::Ask { .. })
128 }
129
130 #[must_use]
132 pub fn redirect_target(&self) -> Option<(String, u16)> {
133 match self {
134 Self::Moved { host, port, .. } | Self::Ask { host, port, .. } => {
135 Some((host.clone(), *port))
136 }
137 _ => None,
138 }
139 }
140
141 #[must_use]
143 pub const fn redirect_slot(&self) -> Option<u16> {
144 match self {
145 Self::Moved { slot, .. } | Self::Ask { slot, .. } => Some(*slot),
146 _ => None,
147 }
148 }
149}