1use alloc::{
4 boxed::Box,
5 format,
6 string::{String, ToString as _},
7};
8use core::fmt;
9use std::{ffi::OsString, io};
10
11macro_rules! format_err {
12 ($($tt:tt)*) => {
13 crate::Error::new(alloc::format!($($tt)*))
14 };
15}
16
17macro_rules! bail {
18 ($($tt:tt)*) => {
19 return Err(format_err!($($tt)*))
20 };
21}
22
23pub(crate) type Result<T, E = Error> = core::result::Result<T, E>;
24
25#[derive(Debug)]
27pub struct Error(ErrorKind);
28
29#[derive(Debug)]
34pub(crate) enum ErrorKind {
35 Io(io::Error),
36
37 CfgExprParse(crate::cfg_expr::error::ParseError),
38
39 Other(Box<str>),
40 WithContext(Box<str>, Option<Box<dyn std::error::Error + Send + Sync>>),
41}
42
43impl Error {
44 pub(crate) fn new(e: impl Into<ErrorKind>) -> Self {
45 Self(e.into())
46 }
47
48 pub(crate) fn env_not_unicode(name: &str, var: OsString) -> Self {
49 Self(ErrorKind::WithContext(
50 format!("failed to parse environment variable `{name}`").into_boxed_str(),
51 Some(Box::new(std::env::VarError::NotUnicode(var))),
52 ))
53 }
54 pub(crate) fn env_not_unicode_redacted(name: &str) -> Self {
55 Self(ErrorKind::WithContext(
56 format!("failed to parse environment variable `{name}`").into_boxed_str(),
57 Some("environment variable was not valid unicode: [REDACTED]".into()),
58 ))
59 }
60}
61
62impl fmt::Display for Error {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match &self.0 {
65 ErrorKind::Io(e) => fmt::Display::fmt(e, f),
66 ErrorKind::CfgExprParse(e) => fmt::Display::fmt(e, f),
67 ErrorKind::Other(e) | ErrorKind::WithContext(e, ..) => fmt::Display::fmt(e, f),
68 }
69 }
70}
71
72impl std::error::Error for Error {
73 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
74 match &self.0 {
75 ErrorKind::Io(e) => e.source(),
76 ErrorKind::CfgExprParse(e) => e.source(),
77 ErrorKind::Other(_) => None,
78 ErrorKind::WithContext(_, e) => Some(&**e.as_ref()?),
79 }
80 }
81}
82
83impl From<Error> for io::Error {
85 fn from(e: Error) -> Self {
86 match e.0 {
87 ErrorKind::Io(e) => e,
88 ErrorKind::CfgExprParse(e) => Self::other(e),
89 ErrorKind::Other(e) | ErrorKind::WithContext(e, None) => Self::other(e.into_string()),
90 ErrorKind::WithContext(msg, Some(source)) => {
91 let kind = if let Some(e) = source.downcast_ref::<io::Error>() {
92 e.kind()
93 } else if source.downcast_ref::<toml::de::Error>().is_some() {
94 io::ErrorKind::InvalidData
95 } else {
96 io::ErrorKind::Other
97 };
98 Self::new(kind, Error(ErrorKind::WithContext(msg, Some(source))))
99 }
100 }
101 }
102}
103
104impl From<String> for ErrorKind {
105 fn from(s: String) -> Self {
106 Self::Other(s.into_boxed_str())
107 }
108}
109impl From<crate::cfg_expr::error::ParseError> for ErrorKind {
110 fn from(e: crate::cfg_expr::error::ParseError) -> Self {
111 Self::CfgExprParse(e)
112 }
113}
114
115impl From<io::Error> for Error {
122 fn from(e: io::Error) -> Self {
123 Self(ErrorKind::Io(e))
124 }
125}
126impl From<std::env::VarError> for Error {
128 fn from(e: std::env::VarError) -> Self {
129 Self(ErrorKind::Other(e.to_string().into_boxed_str()))
130 }
131}
132
133pub(crate) trait Context<T, E> {
135 fn context<C>(self, context: C) -> Result<T, Error>
136 where
137 C: fmt::Display;
138 fn with_context<C, F>(self, context: F) -> Result<T, Error>
139 where
140 C: fmt::Display,
141 F: FnOnce() -> C;
142}
143impl<T, E> Context<T, E> for Result<T, E>
144where
145 E: std::error::Error + Send + Sync + 'static,
146{
147 fn context<C>(self, context: C) -> Result<T, Error>
148 where
149 C: fmt::Display,
150 {
151 match self {
152 Ok(ok) => Ok(ok),
153 Err(e) => Err(Error(ErrorKind::WithContext(
154 context.to_string().into_boxed_str(),
155 Some(Box::new(e)),
156 ))),
157 }
158 }
159 fn with_context<C, F>(self, context: F) -> Result<T, Error>
160 where
161 C: fmt::Display,
162 F: FnOnce() -> C,
163 {
164 match self {
165 Ok(ok) => Ok(ok),
166 Err(e) => Err(Error(ErrorKind::WithContext(
167 context().to_string().into_boxed_str(),
168 Some(Box::new(e)),
169 ))),
170 }
171 }
172}
173impl<T> Context<T, core::convert::Infallible> for Option<T> {
174 fn context<C>(self, context: C) -> Result<T, Error>
175 where
176 C: fmt::Display,
177 {
178 match self {
179 Some(ok) => Ok(ok),
180 None => Err(Error(ErrorKind::WithContext(context.to_string().into_boxed_str(), None))),
181 }
182 }
183 fn with_context<C, F>(self, context: F) -> Result<T, Error>
184 where
185 C: fmt::Display,
186 F: FnOnce() -> C,
187 {
188 match self {
189 Some(ok) => Ok(ok),
190 None => {
191 Err(Error(ErrorKind::WithContext(context().to_string().into_boxed_str(), None)))
192 }
193 }
194 }
195}