Skip to main content

target_spec_json/
error.rs

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