1use std::{fmt, io};
11
12use futures_channel::mpsc;
13use thiserror::Error;
14
15#[cfg(feature = "backtrace")]
16use crate::proto::{ExtBacktrace, trace};
17#[cfg(feature = "__dnssec")]
18use hickory_proto::dnssec::{DnsSecError, DnsSecErrorKind};
19use hickory_proto::{ProtoError, ProtoErrorKind};
20
21#[derive(Debug, Error)]
23#[non_exhaustive]
24pub enum ErrorKind {
25 #[error("{0}")]
27 Message(&'static str),
28
29 #[error("{0}")]
31 Msg(String),
32
33 #[cfg(feature = "__dnssec")]
36 #[error("dnssec error")]
37 DnsSec(#[from] DnsSecError),
38
39 #[error("io error")]
41 Io(#[from] std::io::Error),
42
43 #[error("proto error")]
45 Proto(#[from] ProtoError),
46
47 #[error("error sending to mpsc: {0}")]
49 SendError(#[from] mpsc::SendError),
50
51 #[error("request timed out")]
53 Timeout,
54}
55
56impl Clone for ErrorKind {
57 fn clone(&self) -> Self {
58 use self::ErrorKind::*;
59 match self {
60 Message(msg) => Message(msg),
61 Msg(msg) => Msg(msg.clone()),
62 #[cfg(feature = "__dnssec")]
64 DnsSec(dnssec) => DnsSec(dnssec.clone()),
65 Io(io) => Io(std::io::Error::from(io.kind())),
66 Proto(proto) => Proto(proto.clone()),
67 SendError(e) => SendError(e.clone()),
68 Timeout => Timeout,
69 }
70 }
71}
72
73#[derive(Debug, Error, Clone)]
75pub struct Error {
76 kind: ErrorKind,
77 #[cfg(feature = "backtrace")]
78 backtrack: Option<ExtBacktrace>,
79}
80
81impl Error {
82 pub fn kind(&self) -> &ErrorKind {
84 &self.kind
85 }
86}
87
88impl fmt::Display for Error {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 cfg_if::cfg_if! {
91 if #[cfg(feature = "backtrace")] {
92 if let Some(backtrace) = &self.backtrack {
93 fmt::Display::fmt(&self.kind, f)?;
94 fmt::Debug::fmt(backtrace, f)
95 } else {
96 fmt::Display::fmt(&self.kind, f)
97 }
98 } else {
99 fmt::Display::fmt(&self.kind, f)
100 }
101 }
102 }
103}
104
105impl From<ErrorKind> for Error {
106 fn from(kind: ErrorKind) -> Self {
107 Self {
108 kind,
109 #[cfg(feature = "backtrace")]
110 backtrack: trace!(),
111 }
112 }
113}
114
115impl From<&'static str> for Error {
116 fn from(msg: &'static str) -> Self {
117 ErrorKind::Message(msg).into()
118 }
119}
120
121impl From<mpsc::SendError> for Error {
122 fn from(e: mpsc::SendError) -> Self {
123 ErrorKind::from(e).into()
124 }
125}
126
127impl From<String> for Error {
128 fn from(msg: String) -> Self {
129 ErrorKind::Msg(msg).into()
130 }
131}
132
133#[cfg(feature = "__dnssec")]
134impl From<DnsSecError> for Error {
135 fn from(e: DnsSecError) -> Self {
136 match e.kind() {
137 DnsSecErrorKind::Timeout => ErrorKind::Timeout.into(),
138 _ => ErrorKind::from(e).into(),
139 }
140 }
141}
142
143impl From<io::Error> for Error {
144 fn from(e: io::Error) -> Self {
145 match e.kind() {
146 io::ErrorKind::TimedOut => ErrorKind::Timeout.into(),
147 _ => ErrorKind::from(e).into(),
148 }
149 }
150}
151
152impl From<ProtoError> for Error {
153 fn from(e: ProtoError) -> Self {
154 match e.kind() {
155 ProtoErrorKind::Timeout => ErrorKind::Timeout.into(),
156 _ => ErrorKind::from(e).into(),
157 }
158 }
159}
160
161impl From<Error> for io::Error {
162 fn from(e: Error) -> Self {
163 match e.kind() {
164 ErrorKind::Timeout => Self::new(io::ErrorKind::TimedOut, e),
165 _ => Self::new(io::ErrorKind::Other, e),
166 }
167 }
168}
169
170#[test]
171fn test_conversion() {
172 let io_error = io::Error::new(io::ErrorKind::TimedOut, "mock timeout");
173
174 let error = Error::from(io_error);
175
176 match error.kind() {
177 ErrorKind::Timeout => (),
178 _ => panic!("incorrect type: {}", error),
179 }
180}