Skip to main content

mill_rpc_core/
error.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4/// RPC status codes (inspired by gRPC).
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[repr(u16)]
7pub enum RpcStatus {
8    Ok = 0,
9    Cancelled = 1,
10    InvalidArgument = 2,
11    NotFound = 3,
12    AlreadyExists = 4,
13    PermissionDenied = 5,
14    Unauthenticated = 6,
15    ResourceExhausted = 7,
16    Internal = 8,
17    Unavailable = 9,
18    DeadlineExceeded = 10,
19}
20
21impl fmt::Display for RpcStatus {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            RpcStatus::Ok => write!(f, "OK"),
25            RpcStatus::Cancelled => write!(f, "CANCELLED"),
26            RpcStatus::InvalidArgument => write!(f, "INVALID_ARGUMENT"),
27            RpcStatus::NotFound => write!(f, "NOT_FOUND"),
28            RpcStatus::AlreadyExists => write!(f, "ALREADY_EXISTS"),
29            RpcStatus::PermissionDenied => write!(f, "PERMISSION_DENIED"),
30            RpcStatus::Unauthenticated => write!(f, "UNAUTHENTICATED"),
31            RpcStatus::ResourceExhausted => write!(f, "RESOURCE_EXHAUSTED"),
32            RpcStatus::Internal => write!(f, "INTERNAL"),
33            RpcStatus::Unavailable => write!(f, "UNAVAILABLE"),
34            RpcStatus::DeadlineExceeded => write!(f, "DEADLINE_EXCEEDED"),
35        }
36    }
37}
38
39/// Structured RPC error with status code and message.
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct RpcError {
42    pub status: RpcStatus,
43    pub message: String,
44}
45
46impl RpcError {
47    pub fn new(status: RpcStatus, message: impl Into<String>) -> Self {
48        Self {
49            status,
50            message: message.into(),
51        }
52    }
53
54    pub fn internal(message: impl Into<String>) -> Self {
55        Self::new(RpcStatus::Internal, message)
56    }
57
58    pub fn invalid_argument(message: impl Into<String>) -> Self {
59        Self::new(RpcStatus::InvalidArgument, message)
60    }
61
62    pub fn not_found(message: impl Into<String>) -> Self {
63        Self::new(RpcStatus::NotFound, message)
64    }
65
66    pub fn method_not_found(method_id: u16) -> Self {
67        Self::new(
68            RpcStatus::NotFound,
69            format!("Method not found: {}", method_id),
70        )
71    }
72
73    pub fn service_not_found(service_id: u16) -> Self {
74        Self::new(
75            RpcStatus::NotFound,
76            format!("Service not found: {}", service_id),
77        )
78    }
79
80    pub fn codec_error(message: impl Into<String>) -> Self {
81        Self::new(RpcStatus::Internal, message)
82    }
83
84    pub fn unavailable(message: impl Into<String>) -> Self {
85        Self::new(RpcStatus::Unavailable, message)
86    }
87
88    pub fn deadline_exceeded(message: impl Into<String>) -> Self {
89        Self::new(RpcStatus::DeadlineExceeded, message)
90    }
91}
92
93impl fmt::Display for RpcError {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        write!(f, "[{}] {}", self.status, self.message)
96    }
97}
98
99impl std::error::Error for RpcError {}
100
101impl From<std::io::Error> for RpcError {
102    fn from(err: std::io::Error) -> Self {
103        RpcError::internal(err.to_string())
104    }
105}
106
107impl From<bincode::Error> for RpcError {
108    fn from(err: bincode::Error) -> Self {
109        RpcError::codec_error(format!("bincode: {}", err))
110    }
111}