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}