identity_core/common/
single_struct_error.rs

1// Copyright 2020-2022 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use std::borrow::Cow;
5use std::error::Error;
6use std::fmt::Debug;
7use std::fmt::Display;
8
9/// A container implementing the [`std::error::Error`] trait.
10///
11/// Instances always carry a corresponding `kind` of type `T` and may be extended with a custom error
12/// message and source.
13///
14/// This type is mainly designed to accommodate for the [single struct error design pattern](https://nrc.github.io/error-docs/error-design/error-type-design.html#single-struct-style).
15///
16/// When used in a specialized context it is recommended to use a type alias (i.e. `type MyError =
17/// SingleStructError<MyErrorKind>`).
18#[derive(Debug)]
19pub struct SingleStructError<T: Debug + Display> {
20  repr: Repr<T>,
21}
22
23impl<T: Display + Debug> Display for SingleStructError<T> {
24  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25    match self.repr {
26      Repr::Simple(ref kind) => write!(f, "{kind}"),
27      Repr::Extensive(ref extensive) => {
28        write!(f, "{}", &extensive.kind)?;
29        let Some(ref message) = extensive.message else {
30          return Ok(());
31        };
32        write!(f, " message: {}", message.as_ref())
33      }
34    }
35  }
36}
37
38impl<T: Debug + Display> Error for SingleStructError<T> {
39  fn source(&self) -> Option<&(dyn Error + 'static)> {
40    self.extensive().and_then(|err| {
41      err
42        .source
43        .as_ref()
44        .map(|source| source.as_ref() as &(dyn Error + 'static))
45    })
46  }
47}
48
49#[derive(Debug)]
50struct Extensive<T: Debug + Display> {
51  kind: T,
52  source: Option<Box<dyn Error + Send + Sync + 'static>>,
53  message: Option<Cow<'static, str>>,
54}
55
56#[derive(Debug)]
57enum Repr<T: Debug + Display> {
58  Simple(T),
59  Extensive(Box<Extensive<T>>),
60}
61
62impl<T: Debug + Display> From<T> for SingleStructError<T> {
63  fn from(kind: T) -> Self {
64    Self::new(kind)
65  }
66}
67
68impl<T: Debug + Display> From<Box<Extensive<T>>> for SingleStructError<T> {
69  fn from(extensive: Box<Extensive<T>>) -> Self {
70    Self {
71      repr: Repr::Extensive(extensive),
72    }
73  }
74}
75
76impl<T: Debug + Display> SingleStructError<T> {
77  /// Constructs a new [`SingleStructError`].  
78  pub fn new(kind: T) -> Self {
79    Self {
80      repr: Repr::Simple(kind),
81    }
82  }
83
84  /// Returns a reference to the corresponding `kind` of this error.
85  pub fn kind(&self) -> &T {
86    match self.repr {
87      Repr::Simple(ref cause) => cause,
88      Repr::Extensive(ref extensive) => &extensive.kind,
89    }
90  }
91
92  /// Converts this error into the corresponding `kind` of this error.
93  pub fn into_kind(self) -> T {
94    match self.repr {
95      Repr::Simple(cause) => cause,
96      Repr::Extensive(extensive) => extensive.kind,
97    }
98  }
99
100  /// Returns a reference to the custom message of the [`SingleStructError`] if it was set.
101  pub fn custom_message(&self) -> Option<&str> {
102    self
103      .extensive()
104      .into_iter()
105      .flat_map(|extensive| extensive.message.as_deref())
106      .next()
107  }
108
109  /// Returns a reference to the attached source of the [`SingleStructError`] if it was set.
110  pub fn source_ref(&self) -> Option<&(dyn Error + Send + Sync + 'static)> {
111    self.extensive().and_then(|extensive| extensive.source.as_deref())
112  }
113
114  /// Converts this error into the source error if it was set.
115  pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync + 'static>> {
116    self.into_extensive().source
117  }
118
119  fn extensive(&self) -> Option<&Extensive<T>> {
120    match self.repr {
121      Repr::Extensive(ref extensive) => Some(extensive.as_ref()),
122      _ => None,
123    }
124  }
125
126  fn into_extensive(self) -> Box<Extensive<T>> {
127    match self.repr {
128      Repr::Extensive(extensive) => extensive,
129      Repr::Simple(kind) => Box::new(Extensive {
130        kind,
131        source: None,
132        message: None,
133      }),
134    }
135  }
136
137  /// Updates the `source` of the [`SingleStructError`].
138  pub fn with_source(self, source: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
139    self._with_source(source.into())
140  }
141
142  fn _with_source(self, source: Box<dyn Error + Send + Sync + 'static>) -> Self {
143    let mut extensive = self.into_extensive();
144    extensive.as_mut().source = Some(source);
145    Self::from(extensive)
146  }
147
148  /// Updates the custom message of the [`SingleStructError`].
149  pub fn with_custom_message(self, message: impl Into<Cow<'static, str>>) -> Self {
150    self._with_custom_message(message.into())
151  }
152
153  fn _with_custom_message(self, message: Cow<'static, str>) -> Self {
154    let mut extensive = self.into_extensive();
155    extensive.as_mut().message = Some(message);
156    Self::from(extensive)
157  }
158}