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("Sentinel error: {0}")]
68 Sentinel(String),
69
70 #[error("Authentication failed: {0}")]
72 Auth(String),
73
74 #[error("Pool error: {0}")]
76 Pool(String),
77
78 #[error("Maximum retry attempts ({0}) exceeded")]
80 MaxRetriesExceeded(usize),
81
82 #[error("Unexpected response: {0}")]
84 UnexpectedResponse(String),
85}
86
87impl RedisError {
88 #[must_use]
90 pub fn parse_redirect(msg: &str) -> Option<Self> {
91 if let Some(moved_str) = msg.strip_prefix("MOVED ") {
92 let parts: Vec<&str> = moved_str.split_whitespace().collect();
93 if parts.len() == 2 {
94 if let Ok(slot) = parts[0].parse::<u16>() {
95 if let Some((host, port)) = parts[1].rsplit_once(':') {
96 if let Ok(port) = port.parse::<u16>() {
97 return Some(Self::Moved {
98 slot,
99 host: host.to_string(),
100 port,
101 });
102 }
103 }
104 }
105 }
106 }
107
108 if let Some(ask_str) = msg.strip_prefix("ASK ") {
109 let parts: Vec<&str> = ask_str.split_whitespace().collect();
110 if parts.len() == 2 {
111 if let Ok(slot) = parts[0].parse::<u16>() {
112 if let Some((host, port)) = parts[1].rsplit_once(':') {
113 if let Ok(port) = port.parse::<u16>() {
114 return Some(Self::Ask {
115 slot,
116 host: host.to_string(),
117 port,
118 });
119 }
120 }
121 }
122 }
123 }
124
125 None
126 }
127
128 #[must_use]
130 pub const fn is_redirect(&self) -> bool {
131 matches!(self, Self::Moved { .. } | Self::Ask { .. })
132 }
133
134 #[must_use]
136 pub fn redirect_target(&self) -> Option<(String, u16)> {
137 match self {
138 Self::Moved { host, port, .. } | Self::Ask { host, port, .. } => {
139 Some((host.clone(), *port))
140 }
141 _ => None,
142 }
143 }
144
145 #[must_use]
147 pub const fn redirect_slot(&self) -> Option<u16> {
148 match self {
149 Self::Moved { slot, .. } | Self::Ask { slot, .. } => Some(*slot),
150 _ => None,
151 }
152 }
153}