Skip to main content

dbt_yaml/
shouldbe.rs

1//! This module defines the `ShouldBe` type, which can be used as an error
2//! recovery mechanism during deserialization.
3//!
4//! See the [ShouldBe] documentation for more details.
5
6use std::{
7    fmt::Debug,
8    sync::{
9        atomic::{self, AtomicPtr},
10        Arc,
11    },
12};
13
14use serde::{
15    de::{DeserializeOwned, Error as _},
16    Deserialize, Deserializer, Serialize,
17};
18
19use crate::{Error, Value};
20
21/// Represents a value that "should be" deserialized to type `T`, or provides
22/// information about why it failed to.
23///
24/// This wrapper type can be used as an error recovery mechanism in
25/// `#[derive(Deserialize)]` structs to "containerize" local failures, without
26/// failing the deserialization process: deserializing into a `ShouldBe<T>` will
27/// *always* succeed, producing a [ShouldBe] object that either wraps a valid
28/// `T` value, or the error (and the corresponding pre-deserialized value, if
29/// deserializing from a [Value]) that caused the failure.
30///
31/// You can think of [`ShouldBe<T>`] as a more versatile `Result<T, Error>` that
32/// exposes the equality, ordering, hashing, cloning, and (de)serialization
33/// semantics of `T` (and indeed, [`ShouldBe<T>`] is `Into<Result<T, Error>>`),
34/// while also providing a more ergonomic API for inspecting the error case.
35///
36/// ## Example
37///
38/// ```
39/// # use dbt_yaml::{ShouldBe, Value};
40/// # use serde_derive::{Serialize, Deserialize};
41/// use serde::{Serialize as _, Deserialize as _};
42///
43/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
44/// struct Inner {
45///     field: i32,
46/// }
47///
48/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
49/// struct Outer {
50///    items: Vec<ShouldBe<Inner>>,
51/// }
52///
53/// fn main() -> Result<(), dbt_yaml::Error> {
54///    let yaml = r#"
55///        items:
56///          - field: 1
57///          - field: "2"
58///          - x: 3
59///    "#;
60///    let value: Value = dbt_yaml::from_str(&yaml)?;
61///
62///    let outer: Outer = value.into_typed(|_, _, _| {}, |_| Ok(None))?;
63///    assert_eq!(outer.items.len(), 3);
64///    assert_eq!(outer.items[0].as_ref(), Some(&Inner { field: 1 }));
65///    assert!(outer.items[1].isnt());
66///    assert_eq!(outer.items[1].as_err_msg().unwrap(),
67///               "invalid type: string \"2\", expected i32 at line 4 column 19");
68///    assert!(outer.items[2].isnt());
69///    assert_eq!(outer.items[2].as_err_msg().unwrap(),
70///               "missing field `field` at line 5 column 12");
71///
72///    Ok(())
73/// }
74/// ```
75///
76/// # Clone semantics
77///
78/// [`ShouldBe<T>`] is cloneable as long as `T` is cloneable. Cloning a
79/// [ShouldBe::AndIs] variant clones the inner `T` value. Cloning a
80/// [ShouldBe::ButIsnt] variant, however, does *not* clone the inner [Error], as
81/// [Error] is not cloneable. Instead, the cloned [ShouldBe::ButIsnt] will share
82/// the same underlying [Error] instance as the original. The same is true for
83/// the raw [Value] stored within a [ShouldBe::ButIsnt], if one exists.
84///
85/// # Inspecting errors
86///
87/// [ShouldBe] provides two methods to inspect the error that caused a failed
88/// deserialization: [ShouldBe::as_err_msg] and [ShouldBe::take_err], which
89/// return the error message and the original [Error] instance, respectively.
90///
91/// If you only need the error message, then [ShouldBe::as_err_msg] is always
92/// available on a [ShouldBe::ButIsnt] variant and can be called at any time. If
93/// you need the original [Error] instance, however, you must be aware of the
94/// ownership semantics of [ShouldBe::take_err]: the captured [Error] instance
95/// within a [ShouldBe::ButIsnt] is never directly observable from outside, and
96/// the only way to access it is by extracting it via [ShouldBe::take_err]. This
97/// method transfers ownership of the [Error] out of the [ShouldBe] instance to
98/// the caller. This means that [ShouldBe::take_err] will return `Some(Error)`
99/// *only* the first time it is called on *all [ShouldBe] instances cloned from
100/// the same [ShouldBe::ButIsnt] instance* (see "Clone semantics"); subsequent
101/// calls will return `None`. This is generally what you'd want when handling
102/// errors, as it guarantees that each unique error is only handled once
103/// regardless of how many times the [ShouldBe::ButIsnt] instance has been
104/// cloned.
105///
106/// ## Inspecting the raw [Value]
107///
108/// If a [ShouldBe::ButIsnt] instance was deserialized from a [Value], it will
109/// also capture the corresponding [Value] object that failed to deserialize.
110/// You can access it via the [ShouldBe::as_ref_raw] method.
111///
112/// ## Example
113/// ```
114/// # use dbt_yaml::{ShouldBe, Value};
115///
116/// fn main() -> Result<(), dbt_yaml::Error> {
117///    let yaml = "k: v\n";
118///    let value: Value = dbt_yaml::from_str(&yaml)?;
119///    let should_be: ShouldBe<i32> = value.to_typed(|_, _, _| {}, |_| Ok(None))?;
120///
121///    assert!(should_be.isnt());
122///    assert_eq!(should_be.as_err_msg().unwrap(),
123///              "invalid type: map, expected i32 at line 1 column 1");
124///   
125///    let cloned = should_be.clone();
126///    assert!(cloned.isnt());
127///    // Take the error from the original instance
128///    let err = should_be.take_err().unwrap();
129///    assert_eq!(err.location().unwrap().index, 0);
130///    // Subsequent calls to take_err() return None
131///    assert!(should_be.take_err().is_none());
132///    assert!(cloned.take_err().is_none());
133///    // But the error message is still available
134///    assert_eq!(cloned.as_err_msg().unwrap(),
135///             "invalid type: map, expected i32 at line 1 column 1");
136///    // The raw Value is also available
137///    assert_eq!(should_be.as_ref_raw().unwrap(), &value);
138///    assert_eq!(cloned.as_ref_raw().unwrap(), &value);
139///
140///    Ok(())
141/// }
142/// ```
143///
144/// # Serializing a [`ShouldBe<T>`]
145///
146/// You can serialize a [`ShouldBe<T>`] instance as long as `T` is serializable.
147/// When serializing a [ShouldBe::AndIs] variant, the inner `T` value is
148/// serialized as usual. When serializing a [ShouldBe::ButIsnt] variant, if it
149/// contains a raw [Value] (i.e., it was deserialized from a [Value]), then the
150/// raw [Value] is serialized; otherwise, an error is raised and serialization
151/// fails.
152///
153/// ```
154/// # use dbt_yaml::{ShouldBe, Value};
155///
156/// fn main() -> Result<(), dbt_yaml::Error> {
157///   let yaml = "k: v\n";
158///   let value: Value = dbt_yaml::from_str(&yaml)?;
159///   let should_be: ShouldBe<i32> = value.into_typed(|_, _, _| {}, |_| Ok(None))?;
160///
161///   assert!(should_be.isnt());
162///   let serialized = dbt_yaml::to_string(&should_be)?;
163///   assert_eq!(serialized, yaml);
164///
165///   Ok(())
166/// }
167/// ```
168///
169#[derive(Clone)]
170pub enum ShouldBe<T> {
171    /// On successful deserialization, will contain the expected value of type
172    /// `T`.
173    AndIs(T),
174
175    /// On failed deserialization, will contain the error and raw value (if
176    /// deserialized from a [Value]) that caused the failure.
177    ButIsnt(WhyNot),
178}
179
180impl<T> ShouldBe<T> {
181    /// Returns a reference to the inner `T` value if it exists
182    pub fn as_ref(&self) -> Option<&T> {
183        match self {
184            ShouldBe::AndIs(value) => Some(value),
185            ShouldBe::ButIsnt(_) => None,
186        }
187    }
188
189    /// Returns a mutable reference to the inner `T` value if it exists
190    pub fn as_ref_mut(&mut self) -> Option<&mut T> {
191        match self {
192            ShouldBe::AndIs(value) => Some(value),
193            ShouldBe::ButIsnt(_) => None,
194        }
195    }
196
197    /// Returns a reference to the raw [Value] if this object represents a
198    /// failed deserialization.
199    pub fn as_ref_raw(&self) -> Option<&crate::Value> {
200        match self {
201            ShouldBe::AndIs(_) => None,
202            ShouldBe::ButIsnt(why_not) => why_not.as_ref_raw(),
203        }
204    }
205
206    /// Returns the error message if this object represents a failed
207    /// deserialization.
208    pub fn as_err_msg(&self) -> Option<&str> {
209        match self {
210            ShouldBe::AndIs(_) => None,
211            ShouldBe::ButIsnt(why_not) => Some(why_not.as_msg()),
212        }
213    }
214
215    /// True if this object wraps a valid `T` value, false otherwise.
216    pub fn is(&self) -> bool {
217        matches!(self, ShouldBe::AndIs(_))
218    }
219
220    /// True if this object represents a failed deserialization, false
221    /// otherwise.
222    pub fn isnt(&self) -> bool {
223        matches!(self, ShouldBe::ButIsnt(_))
224    }
225
226    /// Consumes self, returning the inner `T` value if it exists.
227    pub fn into_inner(self) -> Option<T> {
228        match self {
229            ShouldBe::AndIs(value) => Some(value),
230            ShouldBe::ButIsnt(_) => None,
231        }
232    }
233
234    /// Extracts the contained [Error] instance, if any.
235    ///
236    /// This method transfers ownership of the [Error] out of the [ShouldBe]
237    /// instance to the caller. See the [ShouldBe] documentation for more
238    /// details.
239    pub fn take_err(&self) -> Option<Error> {
240        match self {
241            ShouldBe::AndIs(_) => None,
242            ShouldBe::ButIsnt(why_not) => why_not.take_err(),
243        }
244    }
245}
246
247impl<T> Debug for ShouldBe<T>
248where
249    T: Debug,
250{
251    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252        match self {
253            ShouldBe::AndIs(value) => value.fmt(f),
254            ShouldBe::ButIsnt(why_not) => {
255                write!(f, "ShouldBe::ButIsnt({:?})", why_not)
256            }
257        }
258    }
259}
260
261impl<T> Default for ShouldBe<T>
262where
263    T: Default,
264{
265    fn default() -> Self {
266        ShouldBe::AndIs(T::default())
267    }
268}
269
270impl<T> From<T> for ShouldBe<T> {
271    fn from(value: T) -> Self {
272        ShouldBe::AndIs(value)
273    }
274}
275
276impl<T> From<ShouldBe<T>> for Option<T> {
277    fn from(should_be: ShouldBe<T>) -> Self {
278        should_be.into_inner()
279    }
280}
281
282impl<T> From<ShouldBe<T>> for Result<T, Error> {
283    fn from(should_be: ShouldBe<T>) -> Self {
284        match should_be {
285            ShouldBe::AndIs(value) => Ok(value),
286            ShouldBe::ButIsnt(why_not) => Err(why_not.into()),
287        }
288    }
289}
290
291impl<T> PartialEq for ShouldBe<T>
292where
293    T: PartialEq,
294{
295    fn eq(&self, other: &Self) -> bool {
296        match (self, other) {
297            (ShouldBe::AndIs(a), ShouldBe::AndIs(b)) => a == b,
298            (ShouldBe::ButIsnt(a), ShouldBe::ButIsnt(b)) => a == b,
299            _ => false,
300        }
301    }
302}
303
304impl<T> Eq for ShouldBe<T> where T: Eq {}
305
306impl<T> PartialOrd for ShouldBe<T>
307where
308    T: PartialOrd,
309{
310    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
311        match (self, other) {
312            (ShouldBe::AndIs(a), ShouldBe::AndIs(b)) => a.partial_cmp(b),
313            (ShouldBe::ButIsnt(a), ShouldBe::ButIsnt(b)) => a.partial_cmp(b),
314            (ShouldBe::AndIs(_), ShouldBe::ButIsnt(_)) => Some(std::cmp::Ordering::Greater),
315            (ShouldBe::ButIsnt(_), ShouldBe::AndIs(_)) => Some(std::cmp::Ordering::Less),
316        }
317    }
318}
319
320impl<T> Ord for ShouldBe<T>
321where
322    T: Ord,
323{
324    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
325        match (self, other) {
326            (ShouldBe::AndIs(a), ShouldBe::AndIs(b)) => a.cmp(b),
327            (ShouldBe::ButIsnt(a), ShouldBe::ButIsnt(b)) => a.cmp(b),
328            (ShouldBe::AndIs(_), ShouldBe::ButIsnt(_)) => std::cmp::Ordering::Greater,
329            (ShouldBe::ButIsnt(_), ShouldBe::AndIs(_)) => std::cmp::Ordering::Less,
330        }
331    }
332}
333
334impl<T> std::hash::Hash for ShouldBe<T>
335where
336    T: std::hash::Hash,
337{
338    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
339        match self {
340            ShouldBe::AndIs(value) => value.hash(state),
341            ShouldBe::ButIsnt(why_not) => why_not.hash(state),
342        }
343    }
344}
345
346impl<T> Serialize for ShouldBe<T>
347where
348    T: Serialize,
349{
350    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
351    where
352        S: serde::Serializer,
353    {
354        match self {
355            ShouldBe::AndIs(value) => value.serialize(serializer),
356            ShouldBe::ButIsnt(why_not) => {
357                if let Some(raw_value) = why_not.as_ref_raw() {
358                    // If we have a raw value, we can serialize it.
359                    raw_value.serialize(serializer)
360                } else {
361                    // Otherwise, we have to raise an error.
362                    Err(serde::ser::Error::custom(
363                        "Cannot serialize `ShouldBe::ButIsnt` without a raw value",
364                    ))
365                }
366            }
367        }
368    }
369}
370
371impl<'de, T> Deserialize<'de> for ShouldBe<T>
372where
373    T: DeserializeOwned,
374{
375    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
376    where
377        D: Deserializer<'de>,
378    {
379        // Communicate to the ValueDeserializers that we are expecting a
380        // `ShouldBe` value.
381        EXPECTING_SHOULD_BE.with(|cell| *cell.borrow_mut() = true);
382
383        match T::deserialize(deserializer) {
384            Ok(value) => Ok(ShouldBe::AndIs(value)),
385            Err(err) => {
386                if let Some((raw, err)) = take_why_not() {
387                    Ok(ShouldBe::ButIsnt(WhyNot::new(Some(raw), err)))
388                } else {
389                    let err = Error::custom(err);
390                    Ok(ShouldBe::ButIsnt(WhyNot::new(None, err)))
391                }
392            }
393        }
394    }
395}
396
397/// An opaque type that captures the reason why a deserialization to a
398/// [`ShouldBe<T>`] failed.
399///
400/// This type is only meant to be used within the [ShouldBe] type.
401#[derive(Clone)]
402pub struct WhyNot(Arc<WhyNotImpl>);
403
404struct WhyNotImpl {
405    /// The raw value that was attempted to be deserialized.
406    ///
407    /// This field will *only* be populated when deserializing from a
408    /// [Value]. When deserializing from other deserializers, this field
409    /// will be `None`.
410    raw: Option<crate::Value>,
411
412    /// The original error that occurred during deserialization.
413    err: AtomicPtr<Error>,
414
415    /// The string form of `err`
416    err_msg: String,
417}
418
419impl WhyNot {
420    /// Creates a new [WhyNot] from the given raw value and error.
421    pub fn new(raw: Option<crate::Value>, err: Error) -> Self {
422        let err_msg = err.to_string();
423        Self(Arc::new(WhyNotImpl {
424            raw,
425            err: AtomicPtr::new(Box::into_raw(Box::new(err))),
426            err_msg,
427        }))
428    }
429
430    fn take_err(&self) -> Option<Error> {
431        let ptr = self
432            .0
433            .err
434            .swap(std::ptr::null_mut(), atomic::Ordering::SeqCst);
435        if ptr.is_null() {
436            None
437        } else {
438            Some(
439                // SAFETY:
440                // - `ptr` was constructed by [Box::into_raw] and never mutated;
441                // - we are the sole owner of the pointer now
442                // so it's safe to reconstruct the Box
443                unsafe { *Box::from_raw(ptr) },
444            )
445        }
446    }
447
448    fn as_ref_raw(&self) -> Option<&crate::Value> {
449        self.0.raw.as_ref()
450    }
451
452    fn as_msg(&self) -> &str {
453        &self.0.err_msg
454    }
455}
456
457// ----- Value semantics for WhyNot -----
458//
459// `WhyNot` instances are treated as the pair `(raw_value: Option<Value>,
460// err_msg: String)` for the purposes of equality, ordering, and hashing.
461// The `Error` instance is ignored.
462
463impl PartialEq for WhyNot {
464    fn eq(&self, other: &Self) -> bool {
465        self.as_ref_raw() == other.as_ref_raw() && self.as_msg() == other.as_msg()
466    }
467}
468
469impl Eq for WhyNot {}
470
471impl PartialOrd for WhyNot {
472    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
473        Some(self.cmp(other))
474    }
475}
476
477impl Ord for WhyNot {
478    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
479        match self.as_ref_raw().partial_cmp(&other.as_ref_raw()) {
480            Some(std::cmp::Ordering::Equal) | None => self.as_msg().cmp(other.as_msg()),
481            Some(ord) => ord,
482        }
483    }
484}
485
486impl std::hash::Hash for WhyNot {
487    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
488        self.as_ref_raw().hash(state);
489        self.as_msg().hash(state);
490    }
491}
492
493// --------------------------------------
494
495impl From<WhyNot> for Error {
496    fn from(why_not: WhyNot) -> Self {
497        if let Some(err) = why_not.take_err() {
498            err
499        } else {
500            Error::custom(why_not.as_msg())
501        }
502    }
503}
504
505impl Drop for WhyNotImpl {
506    fn drop(&mut self) {
507        let ptr = self
508            .err
509            .swap(std::ptr::null_mut(), atomic::Ordering::SeqCst);
510        if !ptr.is_null() {
511            drop(
512                // SAFETY:
513                // - `ptr` was constructed by [Box::into_raw] and never mutated;
514                // - we are the sole owner of the pointer now
515                // so it's safe to reconstruct the Box
516                unsafe { Box::from_raw(ptr) },
517            );
518        }
519    }
520}
521
522impl Debug for WhyNot {
523    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
524        f.debug_struct("WhyNot")
525            .field("raw", &self.as_ref_raw())
526            .field("err_msg", &self.as_msg())
527            .finish()
528    }
529}
530
531#[cfg(feature = "schemars")]
532impl<T> schemars::JsonSchema for ShouldBe<T>
533where
534    T: schemars::JsonSchema,
535{
536    fn schema_name() -> String {
537        T::schema_name()
538    }
539
540    fn json_schema(generator: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
541        T::json_schema(generator)
542    }
543
544    fn is_referenceable() -> bool {
545        T::is_referenceable()
546    }
547
548    fn schema_id() -> std::borrow::Cow<'static, str> {
549        T::schema_id()
550    }
551
552    #[doc(hidden)]
553    fn _schemars_private_non_optional_json_schema(
554        generator: &mut schemars::gen::SchemaGenerator,
555    ) -> schemars::schema::Schema {
556        T::_schemars_private_non_optional_json_schema(generator)
557    }
558
559    #[doc(hidden)]
560    fn _schemars_private_is_option() -> bool {
561        T::_schemars_private_is_option()
562    }
563}
564
565pub(crate) fn is_expecting_should_be_then_reset() -> bool {
566    EXPECTING_SHOULD_BE.with(|cell| cell.replace(false))
567}
568
569fn take_why_not() -> Option<(Value, Error)> {
570    WHY_NOT.with(|cell| cell.borrow_mut().take())
571}
572
573pub(crate) fn set_why_not(raw: Value, err: Error) {
574    WHY_NOT.with(|cell| *cell.borrow_mut() = Some((raw, err)));
575}
576
577thread_local! {
578    static EXPECTING_SHOULD_BE: std::cell::RefCell<bool> = const {std::cell::RefCell::new(false)};
579
580    static WHY_NOT: std::cell::RefCell<Option<(Value, Error)>> = const {std::cell::RefCell::new(None)};
581}