Skip to main content

octra_sqlite/client/
error.rs

1use std::collections::BTreeMap;
2use std::fmt;
3
4use serde_json::Value;
5
6/// Result alias for octra-sqlite client operations.
7pub type Result<T> = std::result::Result<T, Error>;
8
9/// Error returned by octra-sqlite client operations.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Error {
12    kind: ErrorKind,
13    code: Option<String>,
14    message: String,
15    details: Option<BTreeMap<String, Value>>,
16}
17
18/// Stable category for a client error.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[non_exhaustive]
21pub enum ErrorKind {
22    /// Authentication or owner-authorization failure.
23    Authorization,
24    /// Local configuration or option failure.
25    Config,
26    /// Invalid or inconsistent response data.
27    Decode,
28    /// Local filesystem or stream failure.
29    Io,
30    /// OSR1, OSW1, target, or transaction protocol failure.
31    Protocol,
32    /// Submitted transaction receipt reported failure.
33    Receipt,
34    /// Octra RPC rejected or could not satisfy a request.
35    Rpc,
36    /// Receipt or readiness wait exceeded its deadline.
37    Timeout,
38    /// HTTP or custom transport failure.
39    Transport,
40    /// Wallet loading, key validation, or signing failure.
41    Wallet,
42    /// Error without a narrower stable category.
43    Other,
44}
45
46impl Error {
47    /// Construct an uncategorized client error.
48    pub fn new(message: impl Into<String>) -> Self {
49        Self::with_kind(ErrorKind::Other, message)
50    }
51
52    /// Construct an error with a stable broad category.
53    pub fn with_kind(kind: ErrorKind, message: impl Into<String>) -> Self {
54        Self {
55            kind,
56            code: None,
57            message: message.into(),
58            details: None,
59        }
60    }
61
62    pub(crate) fn with_code(
63        kind: ErrorKind,
64        code: impl Into<String>,
65        message: impl Into<String>,
66    ) -> Self {
67        Self {
68            kind,
69            code: Some(code.into()),
70            message: message.into(),
71            details: None,
72        }
73    }
74
75    pub(crate) fn with_code_and_details(
76        kind: ErrorKind,
77        code: impl Into<String>,
78        message: impl Into<String>,
79        details: impl IntoIterator<Item = (impl Into<String>, Value)>,
80    ) -> Self {
81        Self {
82            kind,
83            code: Some(code.into()),
84            message: message.into(),
85            details: Some(
86                details
87                    .into_iter()
88                    .map(|(key, value)| (key.into(), value))
89                    .collect(),
90            ),
91        }
92    }
93
94    pub(crate) fn with_context(mut self, context: impl AsRef<str>) -> Self {
95        self.message = format!("{}; {}", self.message, context.as_ref());
96        self
97    }
98
99    /// Return the stable broad category for this error.
100    pub fn kind(&self) -> ErrorKind {
101        self.kind
102    }
103
104    /// Precise machine-readable code supplied by a remote source or assigned
105    /// at a local protocol boundary.
106    pub fn code(&self) -> Option<&str> {
107        self.code.as_deref()
108    }
109
110    /// Structured details for automation when a local protocol boundary can
111    /// identify resumable context.
112    pub fn details(&self) -> Option<&BTreeMap<String, Value>> {
113        self.details.as_ref()
114    }
115}
116
117impl fmt::Display for Error {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        f.write_str(&self.message)
120    }
121}
122
123impl std::error::Error for Error {}
124
125impl From<crate::protocol::error::Error> for Error {
126    fn from(error: crate::protocol::error::Error) -> Self {
127        Self::with_kind(ErrorKind::Protocol, error.to_string())
128    }
129}
130
131impl From<base64::DecodeError> for Error {
132    fn from(error: base64::DecodeError) -> Self {
133        Self::with_kind(ErrorKind::Decode, error.to_string())
134    }
135}
136
137impl From<hex::FromHexError> for Error {
138    fn from(error: hex::FromHexError) -> Self {
139        Self::with_kind(ErrorKind::Decode, error.to_string())
140    }
141}
142
143impl From<serde_json::Error> for Error {
144    fn from(error: serde_json::Error) -> Self {
145        Self::with_kind(ErrorKind::Decode, error.to_string())
146    }
147}
148
149impl From<std::io::Error> for Error {
150    fn from(error: std::io::Error) -> Self {
151        Self::with_kind(ErrorKind::Io, error.to_string())
152    }
153}
154
155#[cfg(feature = "http")]
156impl From<ureq::Error> for Error {
157    fn from(error: ureq::Error) -> Self {
158        Self::with_kind(ErrorKind::Transport, error.to_string())
159    }
160}