relay_dynamic_config/
error_boundary.rs

1use std::error::Error;
2use std::sync::Arc;
3
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use serde_json::Value;
6
7/// Wraps a serialization / deserialization result to prevent error from bubbling up.
8///
9/// This is useful for keeping errors in an experimental part of a schema contained.
10#[derive(Clone, Debug)]
11pub enum ErrorBoundary<T> {
12    /// Contains the error value.
13    Err(Arc<dyn Error + Send + Sync + 'static>),
14    /// Contains the success value.
15    Ok(T),
16}
17
18impl<T> ErrorBoundary<T> {
19    /// Returns `true` if the result is [`Ok`].
20    #[inline]
21    #[allow(unused)]
22    pub fn is_ok(&self) -> bool {
23        match *self {
24            Self::Ok(_) => true,
25            Self::Err(_) => false,
26        }
27    }
28
29    /// Returns `true` if the result is [`Err`].
30    #[inline]
31    #[allow(unused)]
32    pub fn is_err(&self) -> bool {
33        !self.is_ok()
34    }
35
36    /// Converts from `Result<T, E>` to [`Option<T>`].
37    #[inline]
38    pub fn ok(self) -> Option<T> {
39        match self {
40            ErrorBoundary::Err(_) => None,
41            ErrorBoundary::Ok(value) => Some(value),
42        }
43    }
44
45    /// Converts from `ErrorBoundary<T>` to `ErrorBoundary<&T>`.
46    pub fn as_ref(&self) -> ErrorBoundary<&T> {
47        match self {
48            Self::Ok(t) => ErrorBoundary::Ok(t),
49            Self::Err(e) => ErrorBoundary::Err(Arc::clone(e)),
50        }
51    }
52
53    /// Returns the contained [`Ok`] value or computes it from a closure.
54    #[inline]
55    pub fn unwrap_or_else<F>(self, op: F) -> T
56    where
57        F: FnOnce(&(dyn Error + 'static)) -> T,
58    {
59        match self {
60            Self::Ok(t) => t,
61            Self::Err(e) => op(e.as_ref()),
62        }
63    }
64
65    /// Inserts a value computed from `f` into the error boundary if it is [`Err`],
66    /// then returns a mutable reference to the contained value.
67    pub fn get_or_insert_with<F>(&mut self, f: F) -> &mut T
68    where
69        F: FnOnce() -> T,
70    {
71        if let Self::Err(_) = self {
72            *self = Self::Ok(f());
73        }
74
75        // SAFETY: an `Err` variant for `self` would have been replaced by a `Ok`
76        // variant in the code above.
77        match self {
78            Self::Ok(t) => t,
79            Self::Err(_) => unsafe { std::hint::unreachable_unchecked() },
80        }
81    }
82}
83
84impl<T> Default for ErrorBoundary<T>
85where
86    T: Default,
87{
88    fn default() -> Self {
89        Self::Ok(T::default())
90    }
91}
92
93impl<'de, T> Deserialize<'de> for ErrorBoundary<T>
94where
95    T: Deserialize<'de>,
96{
97    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
98    where
99        D: Deserializer<'de>,
100    {
101        let value = Value::deserialize(deserializer)?;
102        Ok(match T::deserialize(value) {
103            Ok(t) => Self::Ok(t),
104            Err(error) => Self::Err(Arc::new(error)),
105        })
106    }
107}
108
109impl<T> Serialize for ErrorBoundary<T>
110where
111    T: Serialize,
112{
113    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
114    where
115        S: Serializer,
116    {
117        let option = match *self {
118            Self::Ok(ref t) => Some(t),
119            Self::Err(_) => None,
120        };
121
122        option.serialize(serializer)
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn test_deserialize_ok() {
132        let boundary = serde_json::from_str::<ErrorBoundary<u32>>("42").unwrap();
133        assert!(boundary.is_ok());
134    }
135
136    #[test]
137    fn test_deserialize_err() {
138        let boundary = serde_json::from_str::<ErrorBoundary<u32>>("-1").unwrap();
139        assert!(boundary.is_err());
140    }
141
142    #[test]
143    fn test_deserialize_syntax_err() {
144        serde_json::from_str::<ErrorBoundary<u32>>("---")
145            .expect_err("syntax errors should bubble through");
146    }
147
148    #[test]
149    fn test_serialize_ok() {
150        let boundary = ErrorBoundary::Ok(42);
151        assert_eq!(serde_json::to_string(&boundary).unwrap(), "42");
152    }
153
154    #[test]
155    fn test_serialize_err() {
156        let boundary = ErrorBoundary::<u32>::Err(Arc::new(std::fmt::Error));
157        assert_eq!(serde_json::to_string(&boundary).unwrap(), "null");
158    }
159}