Skip to main content

ark_rest/
error.rs

1use crate::conversions::ConversionError;
2use std::error::Error as StdError;
3use std::fmt;
4
5type Source = Box<dyn StdError + Send + Sync + 'static>;
6
7pub struct Error {
8    inner: ErrorImpl,
9}
10
11struct ErrorImpl {
12    kind: Kind,
13    source: Option<Source>,
14}
15
16#[derive(Debug)]
17enum Kind {
18    Request,
19    Conversion,
20}
21
22impl Error {
23    fn new(kind: Kind) -> Self {
24        Self {
25            inner: ErrorImpl { kind, source: None },
26        }
27    }
28
29    pub(crate) fn with(mut self, source: impl Into<Source>) -> Self {
30        self.inner.source = Some(source.into());
31        self
32    }
33
34    pub(crate) fn request(source: impl Into<Source>) -> Self {
35        Error::new(Kind::Request).with(source)
36    }
37
38    pub(crate) fn conversion(source: impl Into<Source>) -> Self {
39        Error::new(Kind::Conversion).with(source)
40    }
41
42    /// Returns `true` if the server rejected the request because the SDK
43    /// version is too old.
44    pub fn is_version_mismatch(&self) -> bool {
45        if let Some(source) = &self.inner.source {
46            return source.to_string().contains("BUILD_VERSION_TOO_OLD");
47        }
48        false
49    }
50
51    fn description(&self) -> &str {
52        match &self.inner.kind {
53            Kind::Request => "request failed",
54            Kind::Conversion => "failed to convert between types",
55        }
56    }
57}
58
59impl fmt::Debug for Error {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        let mut f = f.debug_tuple("ark_grpc::Error");
62
63        f.field(&self.inner.kind);
64
65        if let Some(source) = &self.inner.source {
66            f.field(source);
67        }
68
69        f.finish()
70    }
71}
72
73impl fmt::Display for Error {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        f.write_str(self.description())
76    }
77}
78
79impl StdError for Error {
80    fn source(&self) -> Option<&(dyn StdError + 'static)> {
81        self.inner
82            .source
83            .as_ref()
84            .map(|source| &**source as &(dyn StdError + 'static))
85    }
86}
87
88impl From<ConversionError> for Error {
89    fn from(value: ConversionError) -> Self {
90        Error::conversion(value)
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn is_version_mismatch_true_when_source_contains_marker() {
100        let err = Error::request("BUILD_VERSION_TOO_OLD: upgrade your client");
101        assert!(err.is_version_mismatch());
102    }
103
104    #[test]
105    fn is_version_mismatch_false_for_other_errors() {
106        let err = Error::request("connection refused");
107        assert!(!err.is_version_mismatch());
108    }
109
110    #[test]
111    fn is_version_mismatch_false_when_no_source() {
112        let err = Error::new(Kind::Request);
113        assert!(!err.is_version_mismatch());
114    }
115}