termwiz/
error.rs

1use std::fmt::Display;
2use thiserror::*;
3
4/// The termwiz Error type encapsulates a range of internal
5/// errors in an opaque manner.  You can use the `source`
6/// method to reach the underlying errors if
7/// necessary, but it is not expected that most code will
8/// need to do so.  Please file an issue if you've got a
9/// usecase for this!
10#[derive(Error, Debug)]
11#[error(transparent)]
12pub struct Error(pub(crate) InternalError);
13
14/// A Result whose error type is a termwiz Error
15pub type Result<T> = std::result::Result<T, Error>;
16
17impl<E> From<E> for Error
18where
19    E: Into<InternalError>,
20{
21    fn from(err: E) -> Self {
22        Self(err.into())
23    }
24}
25
26/// This enum encapsulates the various errors that can be
27/// mapped into the termwiz Error type.
28/// The intent is that this is effectively private to termwiz
29/// itself, but since Rust doesn't allow enums with private
30/// variants, we're dancing around with a newtype of an enum
31/// and hiding it from the docs.
32#[derive(Error, Debug)]
33#[non_exhaustive]
34#[doc(hidden)]
35pub enum InternalError {
36    #[error(transparent)]
37    Fmt(#[from] std::fmt::Error),
38
39    #[error(transparent)]
40    Io(#[from] std::io::Error),
41
42    #[error(transparent)]
43    Regex(#[from] fancy_regex::Error),
44
45    #[error(transparent)]
46    FromUtf8(#[from] std::string::FromUtf8Error),
47
48    #[error(transparent)]
49    Utf8(#[from] std::str::Utf8Error),
50
51    #[error(transparent)]
52    Base64(#[from] base64::DecodeError),
53
54    #[error(transparent)]
55    ParseFloat(#[from] std::num::ParseFloatError),
56
57    #[error(transparent)]
58    ParseInt(#[from] std::num::ParseIntError),
59
60    #[error(transparent)]
61    FloatIsNan(#[from] ordered_float::FloatIsNan),
62
63    #[error("{0}")]
64    StringErr(#[from] StringWrap),
65
66    #[error(transparent)]
67    Anyhow(#[from] anyhow::Error),
68
69    #[error(transparent)]
70    Terminfo(#[from] terminfo::Error),
71
72    #[error(transparent)]
73    FileDescriptor(#[from] filedescriptor::Error),
74
75    #[error(transparent)]
76    BlobLease(#[from] wezterm_blob_leases::Error),
77
78    #[cfg(feature = "use_image")]
79    #[error(transparent)]
80    ImageError(#[from] image::ImageError),
81
82    #[error("{}", .context)]
83    Context {
84        context: String,
85        source: Box<dyn std::error::Error + Send + Sync + 'static>,
86    },
87}
88
89impl From<String> for InternalError {
90    fn from(s: String) -> Self {
91        InternalError::StringErr(StringWrap(s))
92    }
93}
94
95#[derive(Error, Debug)]
96#[doc(hidden)]
97#[error("{0}")]
98pub struct StringWrap(pub String);
99
100#[macro_export]
101macro_rules! format_err {
102    ($msg:literal $(,)?) => {
103        return $crate::error::Error::from($crate::error::StringWrap($msg.to_string()))
104    };
105    ($err:expr $(,)?) => {
106        return $crate::error::Error::from($crate::error::StringWrap(format!($err)))
107    };
108    ($fmt:expr, $($arg:tt)*) => {
109        return $crate::error::Error::from($crate::error::StringWrap(format!($fmt, $($arg)*)))
110    };
111}
112
113#[macro_export]
114macro_rules! bail {
115    ($msg:literal $(,)?) => {
116        return Err($crate::error::StringWrap($msg.to_string()).into())
117    };
118    ($err:expr $(,)?) => {
119        return Err($crate::error::StringWrap(format!($err)).into())
120    };
121    ($fmt:expr, $($arg:tt)*) => {
122        return Err($crate::error::StringWrap(format!($fmt, $($arg)*)).into())
123    };
124}
125
126#[macro_export]
127macro_rules! ensure {
128    ($cond:expr, $msg:literal $(,)?) => {
129        if !$cond {
130            return Err($crate::error::StringWrap(format!($msg)).into());
131        }
132    };
133    ($cond:expr, $err:expr $(,)?) => {
134        if !$cond {
135            return Err($crate::error::StringWrap(format!($err)).into());
136        }
137    };
138    ($cond:expr, $fmt:expr, $($arg:tt)*) => {
139        if !$cond {
140            return Err($crate::error::StringWrap(format!($fmt, $($arg)*)).into());
141        }
142    };
143}
144
145/// This trait allows extending the Result type so that it can create a
146/// `termwiz::Error` that wraps an underlying other error and provide
147/// additional context on that error.
148pub trait Context<T, E> {
149    /// Wrap the error value with additional context.
150    fn context<C>(self, context: C) -> Result<T>
151    where
152        C: Display + Send + Sync + 'static;
153
154    /// Wrap the error value with additional context that is evaluated lazily
155    /// only once an error does occur.
156    fn with_context<C, F>(self, f: F) -> Result<T>
157    where
158        C: Display + Send + Sync + 'static,
159        F: FnOnce() -> C;
160}
161
162impl<T, E> Context<T, E> for std::result::Result<T, E>
163where
164    E: std::error::Error + Send + Sync + 'static,
165{
166    fn context<C>(self, context: C) -> Result<T>
167    where
168        C: Display + Send + Sync + 'static,
169    {
170        self.map_err(|error| {
171            Error(InternalError::Context {
172                context: context.to_string(),
173                source: Box::new(error),
174            })
175        })
176    }
177
178    fn with_context<C, F>(self, context: F) -> Result<T>
179    where
180        C: Display + Send + Sync + 'static,
181        F: FnOnce() -> C,
182    {
183        self.map_err(|error| {
184            Error(InternalError::Context {
185                context: context().to_string(),
186                source: Box::new(error),
187            })
188        })
189    }
190}