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 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}