async_nats/
error.rs

1// Copyright 2020-2023 The NATS Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use std::fmt::{Debug, Display};
15
16/// The error type for the NATS client, generic by the kind of error.
17#[derive(Debug)]
18pub struct Error<Kind>
19where
20    Kind: Clone + Debug + Display + PartialEq,
21{
22    pub(crate) kind: Kind,
23    pub(crate) source: Option<crate::Error>,
24}
25
26impl<Kind> Error<Kind>
27where
28    Kind: Clone + Debug + Display + PartialEq,
29{
30    pub(crate) fn new(kind: Kind) -> Self {
31        Self { kind, source: None }
32    }
33
34    pub(crate) fn with_source<S>(kind: Kind, source: S) -> Self
35    where
36        S: Into<crate::Error>,
37    {
38        Self {
39            kind,
40            source: Some(source.into()),
41        }
42    }
43
44    // In some cases the kind doesn't implement `Copy` trait
45    pub fn kind(&self) -> Kind {
46        self.kind.clone()
47    }
48}
49
50impl<Kind> Display for Error<Kind>
51where
52    Kind: Clone + Debug + Display + PartialEq,
53{
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        if let Some(err) = &self.source {
56            write!(f, "{}: {}", self.kind, err)
57        } else {
58            write!(f, "{}", self.kind)
59        }
60    }
61}
62
63impl<Kind> std::error::Error for Error<Kind>
64where
65    Kind: Clone + Debug + Display + PartialEq,
66{
67    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
68        self.source
69            .as_ref()
70            .map(|boxed| boxed.as_ref() as &(dyn std::error::Error + 'static))
71    }
72}
73
74impl<Kind> From<Kind> for Error<Kind>
75where
76    Kind: Clone + Debug + Display + PartialEq,
77{
78    fn from(kind: Kind) -> Self {
79        Self { kind, source: None }
80    }
81}
82
83#[cfg(test)]
84mod test {
85    #![allow(dead_code)]
86
87    use super::*;
88    use std::fmt::Formatter;
89
90    // Define a custom error kind as a public enum
91    #[derive(Clone, Debug, PartialEq)]
92    enum FooErrorKind {
93        Bar,
94        Baz,
95    }
96
97    impl Display for FooErrorKind {
98        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99            match self {
100                Self::Bar => write!(f, "bar error"),
101                Self::Baz => write!(f, "baz error"),
102            }
103        }
104    }
105
106    // Define a custom error type as a public struct
107    type FooError = Error<FooErrorKind>;
108
109    #[test]
110    fn new() {
111        let error = FooError::new(FooErrorKind::Bar);
112        assert_eq!(error.kind, FooErrorKind::Bar);
113        assert!(error.source.is_none());
114    }
115
116    #[test]
117    fn with_source() {
118        let source = std::io::Error::new(std::io::ErrorKind::Other, "foo");
119        let error = FooError::with_source(FooErrorKind::Bar, source);
120        assert_eq!(error.kind, FooErrorKind::Bar);
121        assert_eq!(error.source.unwrap().to_string(), "foo");
122    }
123
124    #[test]
125    fn kind() {
126        let error: FooError = FooErrorKind::Bar.into();
127        let kind = error.kind();
128        // ensure the kind can be invoked multiple times even though Copy is not implemented
129        let _ = error.kind();
130        assert_eq!(kind, FooErrorKind::Bar);
131    }
132
133    #[test]
134    fn display_without_source() {
135        let error: FooError = FooErrorKind::Bar.into();
136        assert_eq!(format!("{}", error), "bar error");
137    }
138
139    #[test]
140    fn from() {
141        let error: FooError = FooErrorKind::Bar.into();
142        assert_eq!(error.kind, FooErrorKind::Bar);
143        assert!(error.source.is_none());
144    }
145}