akv_cli/
error.rs

1// Copyright 2024 Heath Stewart.
2// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
3
4use std::{
5    borrow::{Borrow, Cow},
6    convert::Infallible,
7    fmt,
8};
9
10pub type Result<T> = std::result::Result<T, Error>;
11
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub enum ErrorKind {
14    InvalidData,
15    Io,
16    Other,
17}
18
19impl fmt::Display for ErrorKind {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        // cspell:ignore errno
22        match self {
23            ErrorKind::InvalidData => f.write_str("InvalidData"),
24            ErrorKind::Io => f.write_str("Io"),
25            ErrorKind::Other => f.write_str("Other"),
26        }
27    }
28}
29
30#[derive(Debug)]
31pub struct Error {
32    repr: Repr,
33}
34
35impl Error {
36    /// Constructs a new `Error` boxing another [`std::error::Error`].
37    pub fn new<E>(kind: ErrorKind, error: E) -> Self
38    where
39        E: Into<Box<dyn std::error::Error + Send + Sync>>,
40    {
41        Self {
42            repr: Repr::Custom(Custom {
43                kind,
44                error: error.into(),
45            }),
46        }
47    }
48
49    /// The [`ErrorKind`] of this `Error`.
50    pub fn kind(&self) -> &ErrorKind {
51        match &self.repr {
52            Repr::Simple(kind)
53            | Repr::SimpleMessage(kind, ..)
54            | Repr::Custom(Custom { kind, .. }) => kind,
55        }
56    }
57
58    /// The message provided when this `Error` was constructed, or `None`.
59    pub fn message(&self) -> Option<&str> {
60        match &self.repr {
61            Repr::SimpleMessage(_, message) => Some(message.borrow()),
62            _ => None,
63        }
64    }
65
66    #[must_use]
67    pub fn with_message<C>(kind: ErrorKind, message: C) -> Self
68    where
69        C: Into<Cow<'static, str>>,
70    {
71        Self {
72            repr: Repr::SimpleMessage(kind, message.into()),
73        }
74    }
75
76    #[must_use]
77    pub fn with_message_fn<F, C>(kind: ErrorKind, message: F) -> Self
78    where
79        Self: Sized,
80        F: FnOnce() -> C,
81        C: Into<Cow<'static, str>>,
82    {
83        Self {
84            repr: Repr::SimpleMessage(kind, message().into()),
85        }
86    }
87}
88
89impl fmt::Display for Error {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        match &self.repr {
92            Repr::Simple(kind) => write!(f, "{kind}"),
93            Repr::SimpleMessage(_, message) => write!(f, "{message}"),
94            Repr::Custom(Custom { error, .. }) => write!(f, "{error}"),
95        }
96    }
97}
98
99impl std::error::Error for Error {
100    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
101        match &self.repr {
102            Repr::Custom(Custom { error, .. }) => Some(&**error),
103            _ => None,
104        }
105    }
106}
107
108impl From<ErrorKind> for Error {
109    fn from(kind: ErrorKind) -> Self {
110        Self {
111            repr: Repr::Simple(kind),
112        }
113    }
114}
115
116impl From<String> for Error {
117    fn from(value: String) -> Self {
118        Self::with_message(ErrorKind::Other, value)
119    }
120}
121
122impl From<Infallible> for Error {
123    fn from(_: Infallible) -> Self {
124        panic!("inconceivable")
125    }
126}
127
128impl From<std::io::Error> for Error {
129    fn from(error: std::io::Error) -> Self {
130        Self::new(ErrorKind::Io, error)
131    }
132}
133
134impl From<std::num::ParseIntError> for Error {
135    fn from(error: std::num::ParseIntError) -> Self {
136        Self::new(ErrorKind::InvalidData, error)
137    }
138}
139
140impl From<std::env::VarError> for Error {
141    fn from(error: std::env::VarError) -> Self {
142        Self::new(ErrorKind::Other, error)
143    }
144}
145
146impl From<azure_core::Error> for Error {
147    fn from(error: azure_core::Error) -> Self {
148        Self::new(ErrorKind::Other, error)
149    }
150}
151
152impl From<dotenvy::Error> for Error {
153    fn from(error: dotenvy::Error) -> Self {
154        Self::new(ErrorKind::Other, error)
155    }
156}
157
158impl From<url::ParseError> for Error {
159    fn from(error: url::ParseError) -> Self {
160        Self::new(ErrorKind::InvalidData, error)
161    }
162}
163
164#[derive(Debug)]
165enum Repr {
166    Simple(ErrorKind),
167    SimpleMessage(ErrorKind, Cow<'static, str>),
168    Custom(Custom),
169}
170
171#[derive(Debug)]
172struct Custom {
173    kind: ErrorKind,
174    error: Box<dyn std::error::Error + Send + Sync>,
175}