Skip to main content

confium_daemon/
error.rs

1//! Errors surfaced by the confiumd JSON-RPC daemon.
2//!
3//! Daemon errors fall into two layers:
4//! - [`RpcError`] — JSON-RPC protocol errors, serialized into a
5//!   `"error"` object on the wire (see `protocol.rs`).
6//! - [`DaemonError`] — transport / lifecycle failures that bubble up to
7//!   the listen loop and usually terminate the process.
8
9use snafu::Snafu;
10
11/// JSON-RPC error codes as defined by the spec, plus the
12/// Confium-specific range (−32000 to −32099) for server errors.
13///
14/// Spec: <https://www.jsonrpc.org/specification#error_object>
15pub mod code {
16    /// Invalid JSON was received by the server.
17    pub const PARSE_ERROR: i32 = -32700;
18    /// The JSON sent is not a valid Request object.
19    pub const INVALID_REQUEST: i32 = -32600;
20    /// The method does not exist / is not available.
21    pub const METHOD_NOT_FOUND: i32 = -32601;
22    /// Invalid method parameter(s).
23    pub const INVALID_PARAMS: i32 = -32602;
24    /// Internal JSON-RPC error.
25    pub const INTERNAL_ERROR: i32 = -32603;
26    /// Generic server error (Confium-specific range start).
27    pub const SERVER_ERROR: i32 = -32000;
28    /// A Confium engine operation returned an error.
29    pub const ENGINE_ERROR: i32 = -32001;
30}
31
32/// An error that can be serialized into a JSON-RPC `"error"` object.
33#[derive(Debug, Snafu)]
34#[snafu(visibility(pub))]
35pub enum RpcError {
36    /// The requested method is not registered in the dispatch table.
37    #[snafu(display("Method not found: {method}"))]
38    MethodNotFound { method: String },
39
40    /// The params object was missing required fields or had the wrong
41    /// shape for the method.
42    #[snafu(display("Invalid params: {detail}"))]
43    InvalidParams { detail: String },
44
45    /// The Confium engine returned an error. The message is the
46    /// engine's `Display` string; the numeric sub-code (if the caller
47    /// cares) is logged separately.
48    #[snafu(display("Engine error: {message}"))]
49    Engine { message: String },
50
51    /// Catch-all for unexpected internal failures.
52    #[snafu(display("Internal error: {detail}"))]
53    Internal { detail: String },
54}
55
56impl RpcError {
57    /// Map this error to the JSON-RPC integer code it should be
58    /// reported under.
59    pub fn code(&self) -> i32 {
60        match self {
61            RpcError::MethodNotFound { .. } => code::METHOD_NOT_FOUND,
62            RpcError::InvalidParams { .. } => code::INVALID_PARAMS,
63            RpcError::Engine { .. } => code::ENGINE_ERROR,
64            RpcError::Internal { .. } => code::INTERNAL_ERROR,
65        }
66    }
67}
68
69/// Transport / lifecycle errors that are not reported to the client as
70/// a JSON-RPC error but instead cause the listener or connection loop
71/// to abort.
72#[derive(Debug, Snafu)]
73pub enum DaemonError {
74    #[snafu(display("I/O error: {source}"))]
75    Io { source: std::io::Error },
76
77    #[snafu(display("Failed to serialize response: {source}"))]
78    Serialize { source: serde_json::Error },
79
80    #[snafu(display("Failed to bind listener: {source}"))]
81    Bind { source: std::io::Error },
82
83    #[snafu(display("Shutdown signaled"))]
84    Shutdown,
85}
86
87pub type Result<T> = std::result::Result<T, DaemonError>;
88
89impl From<std::io::Error> for DaemonError {
90    fn from(source: std::io::Error) -> Self {
91        DaemonError::Io { source }
92    }
93}
94
95impl From<serde_json::Error> for DaemonError {
96    fn from(source: serde_json::Error) -> Self {
97        DaemonError::Serialize { source }
98    }
99}