pub trait RpcErrCodec:
Send
+ Sized
+ 'static
+ Unpin {
// Required methods
fn encode<C: Codec>(&self, codec: &C) -> EncodedErr;
fn decode<C: Codec>(codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()>;
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
// Provided method
fn should_failover(&self) -> Result<Option<&str>, ()> { ... }
}Expand description
Serialize and Deserialize trait for custom RpcError
There is only two forms for rpc transport layer, u32 and String, choose one of them.
Because Rust does not allow overlapping impl, we only imple RpcErrCodec trait by default for the following types:
- ()
- from i8 to u32
- String
- nix::errno::Errno
If you use other type as error, you can implement manually:
§Example with serde_derive
use serde_derive::{Serialize, Deserialize};
use razor_stream::{Codec, error::{RpcErrCodec, RpcIntErr, EncodedErr}};
use strum::Display;
#[derive(Serialize, Deserialize, Debug)]
pub enum MyError {
NoSuchFile,
TooManyRequest,
}
impl RpcErrCodec for MyError {
#[inline(always)]
fn encode<C: Codec>(&self, codec: &C) -> EncodedErr {
match codec.encode(self) {
Ok(buf)=>EncodedErr::Buf(buf),
Err(())=>EncodedErr::Rpc(RpcIntErr::Encode),
}
}
#[inline(always)]
fn decode<C: Codec>(codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()> {
if let Err(b) = buf {
return codec.decode(b);
} else {
Err(())
}
}
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}§Example with num_enum
use num_enum::TryFromPrimitive;
use razor_stream::{Codec, error::{RpcErrCodec, RpcIntErr, EncodedErr}};
// Define your error codes as a C-like enum with explicit values
// You can use num_enum's TryFromPrimitive for safer deserialization
#[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)]
#[repr(u32)]
pub enum MyRpcErrorCode {
/// Service is not available
ServiceUnavailable = 1,
/// Request timed out
RequestTimeout = 2,
/// Invalid parameter
InvalidParameter = 3,
/// Resource not found
NotFound = 4,
}
impl RpcErrCodec for MyRpcErrorCode {
#[inline(always)]
fn encode<C: Codec>(&self, _codec: &C) -> EncodedErr {
// Manual conversion to u32 (no IntoPrimitive needed)
let code: u32 = *self as u32;
EncodedErr::Num(code)
}
#[inline(always)]
fn decode<C: Codec>(_codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()> {
if let Ok(code) = buf {
// Using num_enum for safe deserialization (TryFromPrimitive)
return MyRpcErrorCode::try_from(code).map_err(|_| ());
}
Err(())
}
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}Required Methods§
fn encode<C: Codec>(&self, codec: &C) -> EncodedErr
fn decode<C: Codec>(codec: &C, buf: Result<u32, &[u8]>) -> Result<Self, ()>
Provided Methods§
Sourcefn should_failover(&self) -> Result<Option<&str>, ()>
fn should_failover(&self) -> Result<Option<&str>, ()>
Check if this error should trigger a failover/retry and return the redirect address.
This is used by FailoverPool to implement leader redirection for multi-node master-slave services. When a follower node receives a write request, it can return a redirect error with the leader’s address.
Returns:
Ok(Some(addr)): Retry to the specific addressOk(None): Retry to next available node (round-robin or leader election)Err(()): Don’t retry, return error to user
Default implementation returns Err(()), meaning no retry.
§Example
use razor_stream::{Codec, error::{RpcErrCodec, EncodedErr}};
#[derive(Debug, Clone, PartialEq)]
pub enum MyError {
Redirect(String),
NotLeader,
OtherError,
}
impl RpcErrCodec for MyError {
fn encode<C: Codec>(&self, _codec: &C) -> EncodedErr {
// ... encode implementation
}
fn decode<C: Codec>(_codec: &C, _buf: Result<u32, &[u8]>) -> Result<Self, ()> {
// ... decode implementation
}
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
fn should_failover(&self) -> Result<Option<&str>, ()> {
match self {
Self::Redirect(addr) => Ok(Some(addr)),
Self::NotLeader => Ok(None), // Retry to next node
Self::OtherError => Err(()), // Don't retry
}
}
}Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.