target_spec_json/
error.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3use core::fmt;
4use std::io;
5
6pub(crate) type Result<T, E = Error> = core::result::Result<T, E>;
7
8#[derive(Debug)]
9pub struct Error(ErrorKind);
10
11// Hiding error variants from a library's public error type to prevent
12// dependency updates from becoming breaking changes.
13// We can add `is_*` methods that indicate the kind of error if needed, but
14// don't expose dependencies' types directly in the public API.
15#[derive(Debug)]
16pub(crate) enum ErrorKind {
17    Io(io::Error),
18
19    Json(serde_json::Error),
20
21    Other(Box<str>),
22    WithContext(Box<str>, Option<Box<dyn std::error::Error + Send + Sync>>),
23}
24
25impl Error {
26    pub(crate) fn new(e: impl Into<ErrorKind>) -> Self {
27        Self(e.into())
28    }
29}
30
31impl fmt::Display for Error {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match &self.0 {
34            ErrorKind::Io(e) => fmt::Display::fmt(e, f),
35            ErrorKind::Json(e) => fmt::Display::fmt(e, f),
36            ErrorKind::Other(e) | ErrorKind::WithContext(e, ..) => fmt::Display::fmt(e, f),
37        }
38    }
39}
40
41impl std::error::Error for Error {
42    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
43        match &self.0 {
44            ErrorKind::Io(e) => e.source(),
45            ErrorKind::Json(e) => e.source(),
46            ErrorKind::Other(_) => None,
47            ErrorKind::WithContext(_, e) => Some(&**e.as_ref()?),
48        }
49    }
50}
51
52impl From<io::Error> for ErrorKind {
53    fn from(e: io::Error) -> Self {
54        Self::Io(e)
55    }
56}
57impl From<String> for ErrorKind {
58    fn from(e: String) -> Self {
59        Self::Other(e.into_boxed_str())
60    }
61}
62impl From<serde_json::Error> for ErrorKind {
63    fn from(e: serde_json::Error) -> Self {
64        Self::Json(e)
65    }
66}
67
68// Note: Do not implement From<ThirdPartyErrorType> to prevent dependency
69// updates from becoming breaking changes.
70// Implementing `From<StdErrorType>` should also be avoided whenever possible,
71// as it would be a breaking change to remove the implementation if the
72// conversion is no longer needed due to changes in the internal implementation.
73
74// Inspired by anyhow::Context.
75pub(crate) trait Context<T, E> {
76    // fn context<C>(self, context: C) -> Result<T, Error>
77    // where
78    //     C: fmt::Display;
79    fn with_context<C, F>(self, context: F) -> Result<T, Error>
80    where
81        C: fmt::Display,
82        F: FnOnce() -> C;
83}
84impl<T, E> Context<T, E> for Result<T, E>
85where
86    E: std::error::Error + Send + Sync + 'static,
87{
88    // fn context<C>(self, context: C) -> Result<T, Error>
89    // where
90    //     C: fmt::Display,
91    // {
92    //     match self {
93    //         Ok(ok) => Ok(ok),
94    //         Err(e) => Err(Error(ErrorKind::WithContext(
95    //             context.to_string().into_boxed_str(),
96    //             Some(Box::new(e)),
97    //         ))),
98    //     }
99    // }
100    fn with_context<C, F>(self, context: F) -> Result<T, Error>
101    where
102        C: fmt::Display,
103        F: FnOnce() -> C,
104    {
105        match self {
106            Ok(ok) => Ok(ok),
107            Err(e) => Err(Error(ErrorKind::WithContext(
108                context().to_string().into_boxed_str(),
109                Some(Box::new(e)),
110            ))),
111        }
112    }
113}
114impl<T> Context<T, core::convert::Infallible> for Option<T> {
115    // fn context<C>(self, context: C) -> Result<T, Error>
116    // where
117    //     C: fmt::Display,
118    // {
119    //     match self {
120    //         Some(ok) => Ok(ok),
121    //         None => Err(Error(ErrorKind::WithContext(context.to_string().into_boxed_str(), None))),
122    //     }
123    // }
124    fn with_context<C, F>(self, context: F) -> Result<T, Error>
125    where
126        C: fmt::Display,
127        F: FnOnce() -> C,
128    {
129        match self {
130            Some(ok) => Ok(ok),
131            None => {
132                Err(Error(ErrorKind::WithContext(context().to_string().into_boxed_str(), None)))
133            }
134        }
135    }
136}