Skip to main content

razor_stream/
error.rs

1use crate::Codec;
2use std::fmt;
3
4/// "rpc_" prefix is reserved for internal error, you should avoid conflict with it
5pub const RPC_ERR_PREFIX: &'static str = "rpc_";
6
7/// A error type defined by client-side user logic
8///
9/// Due to possible decode
10#[derive(thiserror::Error)]
11pub enum RpcError<E: RpcErrCodec> {
12    User(E),
13    Rpc(RpcIntErr),
14}
15
16impl<E: RpcErrCodec> fmt::Display for RpcError<E> {
17    #[inline]
18    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
19        match self {
20            Self::User(e) => RpcErrCodec::fmt(e, f),
21            Self::Rpc(e) => fmt::Display::fmt(e, f),
22        }
23    }
24}
25
26impl<E: RpcErrCodec> fmt::Debug for RpcError<E> {
27    #[inline]
28    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29        fmt::Display::fmt(self, f)
30    }
31}
32
33impl<E: RpcErrCodec> std::cmp::PartialEq<RpcIntErr> for RpcError<E> {
34    #[inline]
35    fn eq(&self, other: &RpcIntErr) -> bool {
36        if let Self::Rpc(r) = self {
37            if r == other {
38                return true;
39            }
40        }
41        false
42    }
43}
44
45impl<E: RpcErrCodec + PartialEq> std::cmp::PartialEq<E> for RpcError<E> {
46    #[inline]
47    fn eq(&self, other: &E) -> bool {
48        if let Self::User(r) = self {
49            return r == other;
50        }
51        false
52    }
53}
54
55impl<E: RpcErrCodec + PartialEq> std::cmp::PartialEq<RpcError<E>> for RpcError<E> {
56    #[inline]
57    fn eq(&self, other: &Self) -> bool {
58        match self {
59            Self::Rpc(r) => {
60                if let Self::Rpc(o) = other {
61                    return r == o;
62                }
63            }
64            Self::User(r) => {
65                if let Self::User(o) = other {
66                    return r == o;
67                }
68            }
69        }
70        false
71    }
72}
73
74impl<E: RpcErrCodec> From<E> for RpcError<E> {
75    #[inline]
76    fn from(e: E) -> Self {
77        Self::User(e)
78    }
79}
80
81impl From<&str> for RpcError<String> {
82    #[inline]
83    fn from(e: &str) -> Self {
84        Self::User(e.to_string())
85    }
86}
87
88impl<E: RpcErrCodec> From<RpcIntErr> for RpcError<E> {
89    #[inline]
90    fn from(e: RpcIntErr) -> Self {
91        Self::Rpc(e)
92    }
93}
94
95/// Serialize and Deserialize trait for custom RpcError
96///
97/// There is only two forms for rpc transport layer, u32 and String, choose one of them.
98///
99/// Because Rust does not allow overlapping impl, we only imple RpcErrCodec trait by default for the following types:
100/// - ()
101/// - from i8 to u32
102/// - String
103/// - nix::errno::Errno
104///
105/// If you use other type as error, you can implement manually:
106///
107/// # Example with serde_derive
108/// ```rust
109/// use serde_derive::{Serialize, Deserialize};
110/// use razor_stream::{Codec, error::{RpcErrCodec, RpcIntErr, EncodedErr}};
111/// use strum::Display;
112/// #[derive(Serialize, Deserialize, Debug)]
113/// pub enum MyError {
114///     NoSuchFile,
115///     TooManyRequest,
116/// }
117///
118/// impl RpcErrCodec for MyError {
119///     #[inline(always)]
120///     fn encode<C: Codec>(&self, codec: &C) -> EncodedErr {
121///         match codec.encode(self) {
122///             Ok(buf)=>EncodedErr::Buf(buf),
123///             Err(())=>EncodedErr::Rpc(RpcIntErr::Encode),
124///         }
125///     }
126///
127///     #[inline(always)]
128///     fn decode<C: Codec>(codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()> {
129///         if let Err(b) = buf {
130///             return codec.decode(b);
131///         } else {
132///             Err(())
133///         }
134///     }
135///     #[inline(always)]
136///     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
137///         std::fmt::Debug::fmt(self, f)
138///     }
139/// }
140/// ```
141///
142/// # Example with num_enum
143///
144/// ```rust
145/// use num_enum::TryFromPrimitive;
146/// use razor_stream::{Codec, error::{RpcErrCodec, RpcIntErr, EncodedErr}};
147///
148/// // Define your error codes as a C-like enum with explicit values
149/// // You can use num_enum's TryFromPrimitive for safer deserialization
150/// #[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)]
151/// #[repr(u32)]
152/// pub enum MyRpcErrorCode {
153///     /// Service is not available
154///     ServiceUnavailable = 1,
155///     /// Request timed out
156///     RequestTimeout = 2,
157///     /// Invalid parameter
158///     InvalidParameter = 3,
159///     /// Resource not found
160///     NotFound = 4,
161/// }
162///
163/// impl RpcErrCodec for MyRpcErrorCode {
164///     #[inline(always)]
165///     fn encode<C: Codec>(&self, _codec: &C) -> EncodedErr {
166///         // Manual conversion to u32 (no IntoPrimitive needed)
167///         let code: u32 = *self as u32;
168///         EncodedErr::Num(code)
169///     }
170///
171///     #[inline(always)]
172///     fn decode<C: Codec>(_codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()> {
173///         if let Ok(code) = buf {
174///             // Using num_enum for safe deserialization (TryFromPrimitive)
175///             return MyRpcErrorCode::try_from(code).map_err(|_| ());
176///         }
177///         Err(())
178///     }
179///
180///     #[inline(always)]
181///     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
182///         std::fmt::Debug::fmt(self, f)
183///     }
184/// }
185/// ```
186pub trait RpcErrCodec: Send + Sized + 'static + Unpin {
187    fn encode<C: Codec>(&self, codec: &C) -> EncodedErr;
188
189    fn decode<C: Codec>(codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()>;
190
191    /// You can choose to use std::fmt::Debug or std::fmt::Display for the type.
192    ///
193    /// NOTE that this method exists because rust does not have Display for ().
194    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
195}
196
197macro_rules! impl_rpc_error_for_num {
198    ($t: tt) => {
199        impl RpcErrCodec for $t {
200            #[inline(always)]
201            fn encode<C: Codec>(&self, _codec: &C) -> EncodedErr {
202                EncodedErr::Num(*self as u32)
203            }
204
205            #[inline(always)]
206            fn decode<C: Codec>(_codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()> {
207                if let Ok(i) = buf {
208                    if i <= $t::max as u32 {
209                        return Ok(i as Self);
210                    }
211                }
212                Err(())
213            }
214
215            #[inline(always)]
216            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
217                write!(f, "errno {}", self)
218            }
219        }
220    };
221}
222
223impl_rpc_error_for_num!(i8);
224impl_rpc_error_for_num!(u8);
225impl_rpc_error_for_num!(i16);
226impl_rpc_error_for_num!(u16);
227impl_rpc_error_for_num!(i32);
228impl_rpc_error_for_num!(u32);
229
230impl RpcErrCodec for nix::errno::Errno {
231    #[inline(always)]
232    fn encode<C: Codec>(&self, _codec: &C) -> EncodedErr {
233        EncodedErr::Num(*self as u32)
234    }
235
236    #[inline(always)]
237    fn decode<C: Codec>(_codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()> {
238        if let Ok(i) = buf {
239            if i <= i32::max as u32 {
240                return Ok(Self::from_raw(i as i32));
241            }
242        }
243        Err(())
244    }
245
246    #[inline(always)]
247    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
248        write!(f, "{:?}", self)
249    }
250}
251
252impl RpcErrCodec for () {
253    #[inline(always)]
254    fn encode<C: Codec>(&self, _codec: &C) -> EncodedErr {
255        EncodedErr::Num(0u32)
256    }
257
258    #[inline(always)]
259    fn decode<C: Codec>(_codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()> {
260        if let Ok(i) = buf {
261            if i == 0 {
262                return Ok(());
263            }
264        }
265        Err(())
266    }
267
268    #[inline(always)]
269    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270        write!(f, "err")
271    }
272}
273
274impl RpcErrCodec for String {
275    #[inline(always)]
276    fn encode<C: Codec>(&self, _codec: &C) -> EncodedErr {
277        EncodedErr::Buf(Vec::from(self.as_bytes()))
278    }
279    #[inline(always)]
280    fn decode<C: Codec>(_codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()> {
281        if let Err(s) = buf {
282            if let Ok(s) = str::from_utf8(s) {
283                return Ok(s.to_string());
284            }
285        }
286        Err(())
287    }
288
289    #[inline(always)]
290    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
291        write!(f, "{}", self)
292    }
293}
294
295/// RpcIntErr represent internal error from the framework
296///
297/// **NOTE**:
298/// - This error type is serialized in string, "rpc_" prefix is reserved for internal error, you
299/// should avoid conflict with it.
300/// - We presume the variants less than RpcIntErr::Method is retriable errors
301#[derive(
302    strum::Display,
303    strum::EnumString,
304    strum::AsRefStr,
305    PartialEq,
306    PartialOrd,
307    Clone,
308    thiserror::Error,
309)]
310#[repr(u8)]
311pub enum RpcIntErr {
312    /// Ping or connect error
313    #[strum(serialize = "rpc_unreachable")]
314    Unreachable = 0,
315    /// IO error
316    #[strum(serialize = "rpc_io_err")]
317    IO = 1,
318    /// Task timeout
319    #[strum(serialize = "rpc_timeout")]
320    Timeout = 2,
321    /// Method not found
322    #[strum(serialize = "rpc_method_notfound")]
323    Method = 3,
324    /// service notfound
325    #[strum(serialize = "rpc_service_notfound")]
326    Service = 4,
327    /// Encode Error
328    #[strum(serialize = "rpc_encode")]
329    Encode = 5,
330    /// Decode Error
331    #[strum(serialize = "rpc_decode")]
332    Decode = 6,
333    /// Internal error
334    #[strum(serialize = "rpc_internal_err")]
335    Internal = 7,
336    /// invalid version number in rpc header
337    #[strum(serialize = "rpc_invalid_ver")]
338    Version = 8,
339}
340
341// The default Debug derive just ignore strum customized string, by strum only have a Display derive
342impl fmt::Debug for RpcIntErr {
343    #[inline]
344    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
345        fmt::Display::fmt(self, f)
346    }
347}
348
349impl RpcIntErr {
350    #[inline]
351    pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
352        self.as_ref().as_bytes()
353    }
354}
355
356impl From<std::io::Error> for RpcIntErr {
357    #[inline(always)]
358    fn from(_e: std::io::Error) -> Self {
359        Self::IO
360    }
361}
362
363/// A container for error message parse from / send into transport
364#[derive(Debug, thiserror::Error)]
365pub enum EncodedErr {
366    /// The ClientTransport should try the best to parse it from string with "rpc_" prefix
367    Rpc(RpcIntErr),
368    /// For nix errno and the like
369    Num(u32),
370    /// only for server, the ClientTransport will not parse into static type
371    Static(&'static str),
372    /// The ClientTransport will fallback to `Vec<u8>` after try to parse  RpcIntErr and  num
373    Buf(Vec<u8>),
374}
375
376impl EncodedErr {
377    #[inline]
378    pub fn try_as_str<'a>(&'a self) -> Result<&'a str, ()> {
379        match self {
380            Self::Static(s) => return Ok(s),
381            Self::Buf(b) => {
382                if let Ok(s) = str::from_utf8(b) {
383                    return Ok(s);
384                }
385            }
386            _ => {}
387        }
388        Err(())
389    }
390}
391
392/// Just for macro test
393impl std::cmp::PartialEq<EncodedErr> for EncodedErr {
394    fn eq(&self, other: &EncodedErr) -> bool {
395        match self {
396            Self::Rpc(e) => {
397                if let Self::Rpc(o) = other {
398                    return e == o;
399                }
400            }
401            Self::Num(e) => {
402                if let Self::Num(o) = other {
403                    return e == o;
404                }
405            }
406            Self::Static(s) => {
407                if let Ok(o) = other.try_as_str() {
408                    return *s == o;
409                }
410            }
411            Self::Buf(s) => {
412                if let Self::Buf(o) = other {
413                    return s == o;
414                } else if let Ok(o) = other.try_as_str() {
415                    // other's type is not Buf
416                    if let Ok(_s) = str::from_utf8(s) {
417                        return _s == o;
418                    }
419                }
420            }
421        }
422        false
423    }
424}
425
426impl fmt::Display for EncodedErr {
427    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428        match self {
429            Self::Rpc(e) => e.fmt(f),
430            Self::Num(no) => write!(f, "errno {}", no),
431            Self::Static(s) => write!(f, "{}", s),
432            Self::Buf(b) => match str::from_utf8(b) {
433                Ok(s) => {
434                    write!(f, "{}", s)
435                }
436                Err(_) => {
437                    write!(f, "err blob {} length", b.len())
438                }
439            },
440        }
441    }
442}
443
444impl From<RpcIntErr> for EncodedErr {
445    #[inline(always)]
446    fn from(e: RpcIntErr) -> Self {
447        Self::Rpc(e)
448    }
449}
450
451#[cfg(test)]
452mod tests {
453    use super::*;
454    use nix::errno::Errno;
455    use std::str::FromStr;
456
457    #[test]
458    fn test_internal_error() {
459        println!("{}", RpcIntErr::Internal);
460        println!("{:?}", RpcIntErr::Internal);
461        let s = RpcIntErr::Timeout.as_ref();
462        println!("RpcIntErr::Timeout as {}", s);
463        let e = RpcIntErr::from_str(s).expect("parse");
464        assert_eq!(e, RpcIntErr::Timeout);
465        assert!(RpcIntErr::from_str("timeoutss").is_err());
466        assert!(RpcIntErr::Timeout < RpcIntErr::Method);
467        assert!(RpcIntErr::IO < RpcIntErr::Method);
468        assert!(RpcIntErr::Unreachable < RpcIntErr::Method);
469    }
470
471    #[test]
472    fn test_rpc_error_default() {
473        let e = RpcError::<i32>::from(1i32);
474        println!("err {:?} {}", e, e);
475
476        let e = RpcError::<Errno>::from(Errno::EIO);
477        println!("err {:?} {}", e, e);
478
479        let e = RpcError::<String>::from("err_str");
480        println!("err {:?} {}", e, e);
481        let e2 = RpcError::<String>::from("err_str".to_string());
482        assert_eq!(e, e2);
483
484        let e = RpcError::<String>::from(RpcIntErr::IO);
485        println!("err {:?} {}", e, e);
486
487        let _e: Result<(), RpcIntErr> = Err(RpcIntErr::IO);
488
489        // let e: Result<(), RpcError::<String>> = _e.into();
490        // Not allow by rust, and orphan rule prevent we do
491        // `impl<E: RpcErrCodec> From<Result<(), RpcIntErr>> for Result<(), RpcError<E>>`
492
493        // it's ok to use map_err
494        let e: Result<(), RpcError<String>> = _e.map_err(|e| e.into());
495        println!("err {:?}", e);
496    }
497
498    //#[test]
499    //fn test_rpc_error_string_enum() {
500    //    not supported by default, should provide a derive
501    //    #[derive(
502    //        Debug, strum::Display, strum::EnumString, strum::AsRefStr, PartialEq, Clone, thiserror::Error,
503    //    )]
504    //    enum MyError {
505    //        OhMyGod,
506    //    }
507    //    let e = RpcError::<MyError>::from(MyError::OhMyGod);
508    //    println!("err {:?} {}", e, e);
509    //}
510}