1#[cfg(not(feature = "std"))]
11use crate::nostd_prelude::*;
12use crate::StoreError;
13use core::fmt;
14#[cfg(feature = "std")]
15use std::io;
16
17pub type KevyResult<T> = core::result::Result<T, KevyError>;
19
20#[derive(Debug)]
28pub enum KevyError {
29 Store(StoreError),
32 #[cfg(feature = "std")]
34 Io(io::Error),
35 Protocol(String),
39 ReadOnly,
41 InvalidInput(String),
44 NotFound(String),
46 Unsupported(String),
48 TimedOut,
50 Closed,
52}
53
54impl fmt::Display for KevyError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 Self::Store(e) => write!(f, "store error: {e:?}"),
58 #[cfg(feature = "std")]
59 Self::Io(e) => write!(f, "io error: {e}"),
60 Self::Protocol(msg) => write!(f, "protocol error: {msg}"),
61 Self::ReadOnly => {
62 write!(f, "READONLY You can't write against a read only replica")
63 }
64 Self::InvalidInput(msg) => write!(f, "invalid input: {msg}"),
65 Self::NotFound(what) => write!(f, "not found: {what}"),
66 Self::Unsupported(msg) => write!(f, "unsupported: {msg}"),
67 Self::TimedOut => write!(f, "timed out"),
68 Self::Closed => write!(f, "connection closed"),
69 }
70 }
71}
72
73impl core::error::Error for KevyError {
74 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
75 match self {
76 #[cfg(feature = "std")]
77 Self::Io(e) => Some(e),
78 _ => None,
79 }
80 }
81}
82
83impl From<StoreError> for KevyError {
84 fn from(e: StoreError) -> Self {
85 Self::Store(e)
86 }
87}
88
89#[cfg(feature = "std")]
90impl From<io::Error> for KevyError {
91 fn from(e: io::Error) -> Self {
92 Self::Io(e)
93 }
94}
95
96#[cfg(feature = "std")]
104impl From<KevyError> for io::Error {
105 fn from(e: KevyError) -> Self {
106 use io::ErrorKind as K;
107 match e {
108 KevyError::Io(inner) => inner,
109 other => {
110 let kind = match &other {
111 KevyError::Io(_) => unreachable!("moved out above"),
112 KevyError::TimedOut => K::TimedOut,
113 KevyError::Closed => K::ConnectionAborted,
114 KevyError::InvalidInput(_) => K::InvalidInput,
115 KevyError::NotFound(_) => K::NotFound,
116 KevyError::Unsupported(_) => K::Unsupported,
117 KevyError::ReadOnly => K::PermissionDenied,
118 KevyError::Protocol(_) => K::InvalidData,
119 KevyError::Store(StoreError::OutOfMemory) => K::OutOfMemory,
120 KevyError::Store(_) => K::InvalidData,
121 };
122 io::Error::new(kind, other)
123 }
124 }
125 }
126}
127
128#[cfg(all(test, feature = "std"))]
129mod io_interop_tests {
130 use super::*;
131
132 #[test]
136 fn kinds_map_and_the_source_is_the_typed_error() {
137 let cases: [(KevyError, io::ErrorKind); 6] = [
138 (KevyError::TimedOut, io::ErrorKind::TimedOut),
139 (KevyError::Closed, io::ErrorKind::ConnectionAborted),
140 (KevyError::InvalidInput("x".into()), io::ErrorKind::InvalidInput),
141 (KevyError::NotFound("idx".into()), io::ErrorKind::NotFound),
142 (KevyError::Store(StoreError::WrongType), io::ErrorKind::InvalidData),
143 (KevyError::Store(StoreError::OutOfMemory), io::ErrorKind::OutOfMemory),
144 ];
145 for (kevy, kind) in cases {
146 let msg = kevy.to_string();
147 let io: io::Error = kevy.into();
148 assert_eq!(io.kind(), kind, "{msg}");
149 let src = io.get_ref().expect("source preserved");
150 assert!(src.is::<KevyError>(), "downcastable back to KevyError");
151 assert_eq!(src.to_string(), msg, "message survives");
152 }
153 }
154
155 #[test]
157 fn io_passes_through_without_wrapping() {
158 let inner = io::Error::new(io::ErrorKind::BrokenPipe, "pipe");
159 let io: io::Error = KevyError::Io(inner).into();
160 assert_eq!(io.kind(), io::ErrorKind::BrokenPipe);
161 assert!(io.get_ref().is_some_and(|s| !s.is::<KevyError>()), "not re-wrapped");
162 }
163}