pub enum ShouldBe<T> {
AndIs(T),
ButIsnt(WhyNot),
}Expand description
Represents a value that “should be” deserialized to type T, or provides
information about why it failed to.
This wrapper type can be used as an error recovery mechanism in
#[derive(Deserialize)] structs to “containerize” local failures, without
failing the deserialization process: deserializing into a ShouldBe<T> will
always succeed, producing a ShouldBe object that either wraps a valid
T value, or the error (and the corresponding pre-deserialized value, if
deserializing from a Value) that caused the failure.
You can think of ShouldBe<T> as a more versatile Result<T, Error> that
exposes the equality, ordering, hashing, cloning, and (de)serialization
semantics of T (and indeed, ShouldBe<T> is Into<Result<T, Error>>),
while also providing a more ergonomic API for inspecting the error case.
§Example
use serde::{Serialize as _, Deserialize as _};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Inner {
field: i32,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Outer {
items: Vec<ShouldBe<Inner>>,
}
fn main() -> Result<(), dbt_yaml::Error> {
let yaml = r#"
items:
- field: 1
- field: "2"
- x: 3
"#;
let value: Value = dbt_yaml::from_str(&yaml)?;
let outer: Outer = value.into_typed(|_, _, _| {}, |_| Ok(None))?;
assert_eq!(outer.items.len(), 3);
assert_eq!(outer.items[0].as_ref(), Some(&Inner { field: 1 }));
assert!(outer.items[1].isnt());
assert_eq!(outer.items[1].as_err_msg().unwrap(),
"invalid type: string \"2\", expected i32 at line 4 column 19");
assert!(outer.items[2].isnt());
assert_eq!(outer.items[2].as_err_msg().unwrap(),
"missing field `field` at line 5 column 12");
Ok(())
}§Clone semantics
ShouldBe<T> is cloneable as long as T is cloneable. Cloning a
ShouldBe::AndIs variant clones the inner T value. Cloning a
ShouldBe::ButIsnt variant, however, does not clone the inner Error, as
Error is not cloneable. Instead, the cloned ShouldBe::ButIsnt will share
the same underlying Error instance as the original. The same is true for
the raw Value stored within a ShouldBe::ButIsnt, if one exists.
§Inspecting errors
ShouldBe provides two methods to inspect the error that caused a failed deserialization: ShouldBe::as_err_msg and ShouldBe::take_err, which return the error message and the original Error instance, respectively.
If you only need the error message, then ShouldBe::as_err_msg is always
available on a ShouldBe::ButIsnt variant and can be called at any time. If
you need the original Error instance, however, you must be aware of the
ownership semantics of ShouldBe::take_err: the captured Error instance
within a ShouldBe::ButIsnt is never directly observable from outside, and
the only way to access it is by extracting it via ShouldBe::take_err. This
method transfers ownership of the Error out of the ShouldBe instance to
the caller. This means that ShouldBe::take_err will return Some(Error)
only the first time it is called on all ShouldBe instances cloned from
the same ShouldBe::ButIsnt instance (see “Clone semantics”); subsequent
calls will return None. This is generally what you’d want when handling
errors, as it guarantees that each unique error is only handled once
regardless of how many times the ShouldBe::ButIsnt instance has been
cloned.
§Inspecting the raw Value
If a ShouldBe::ButIsnt instance was deserialized from a Value, it will also capture the corresponding Value object that failed to deserialize. You can access it via the ShouldBe::as_ref_raw method.
§Example
fn main() -> Result<(), dbt_yaml::Error> {
let yaml = "k: v\n";
let value: Value = dbt_yaml::from_str(&yaml)?;
let should_be: ShouldBe<i32> = value.to_typed(|_, _, _| {}, |_| Ok(None))?;
assert!(should_be.isnt());
assert_eq!(should_be.as_err_msg().unwrap(),
"invalid type: map, expected i32 at line 1 column 1");
let cloned = should_be.clone();
assert!(cloned.isnt());
// Take the error from the original instance
let err = should_be.take_err().unwrap();
assert_eq!(err.location().unwrap().index, 0);
// Subsequent calls to take_err() return None
assert!(should_be.take_err().is_none());
assert!(cloned.take_err().is_none());
// But the error message is still available
assert_eq!(cloned.as_err_msg().unwrap(),
"invalid type: map, expected i32 at line 1 column 1");
// The raw Value is also available
assert_eq!(should_be.as_ref_raw().unwrap(), &value);
assert_eq!(cloned.as_ref_raw().unwrap(), &value);
Ok(())
}§Serializing a ShouldBe<T>
You can serialize a ShouldBe<T> instance as long as T is serializable.
When serializing a ShouldBe::AndIs variant, the inner T value is
serialized as usual. When serializing a ShouldBe::ButIsnt variant, if it
contains a raw Value (i.e., it was deserialized from a Value), then the
raw Value is serialized; otherwise, an error is raised and serialization
fails.
fn main() -> Result<(), dbt_yaml::Error> {
let yaml = "k: v\n";
let value: Value = dbt_yaml::from_str(&yaml)?;
let should_be: ShouldBe<i32> = value.into_typed(|_, _, _| {}, |_| Ok(None))?;
assert!(should_be.isnt());
let serialized = dbt_yaml::to_string(&should_be)?;
assert_eq!(serialized, yaml);
Ok(())
}Variants§
AndIs(T)
On successful deserialization, will contain the expected value of type
T.
ButIsnt(WhyNot)
On failed deserialization, will contain the error and raw value (if deserialized from a Value) that caused the failure.
Implementations§
Source§impl<T> ShouldBe<T>
impl<T> ShouldBe<T>
Sourcepub fn as_ref_mut(&mut self) -> Option<&mut T>
pub fn as_ref_mut(&mut self) -> Option<&mut T>
Returns a mutable reference to the inner T value if it exists
Sourcepub fn as_ref_raw(&self) -> Option<&Value>
pub fn as_ref_raw(&self) -> Option<&Value>
Returns a reference to the raw Value if this object represents a failed deserialization.
Sourcepub fn as_err_msg(&self) -> Option<&str>
pub fn as_err_msg(&self) -> Option<&str>
Returns the error message if this object represents a failed deserialization.
Sourcepub fn isnt(&self) -> bool
pub fn isnt(&self) -> bool
True if this object represents a failed deserialization, false otherwise.
Sourcepub fn into_inner(self) -> Option<T>
pub fn into_inner(self) -> Option<T>
Consumes self, returning the inner T value if it exists.
Trait Implementations§
Source§impl<'de, T> Deserialize<'de> for ShouldBe<T>where
T: DeserializeOwned,
impl<'de, T> Deserialize<'de> for ShouldBe<T>where
T: DeserializeOwned,
Source§fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where
D: Deserializer<'de>,
Source§impl<T> Ord for ShouldBe<T>where
T: Ord,
impl<T> Ord for ShouldBe<T>where
T: Ord,
Source§impl<T> PartialOrd for ShouldBe<T>where
T: PartialOrd,
impl<T> PartialOrd for ShouldBe<T>where
T: PartialOrd,
impl<T> Eq for ShouldBe<T>where
T: Eq,
Auto Trait Implementations§
impl<T> Freeze for ShouldBe<T>where
T: Freeze,
impl<T> RefUnwindSafe for ShouldBe<T>where
T: RefUnwindSafe,
impl<T> Send for ShouldBe<T>where
T: Send,
impl<T> Sync for ShouldBe<T>where
T: Sync,
impl<T> Unpin for ShouldBe<T>where
T: Unpin,
impl<T> UnsafeUnpin for ShouldBe<T>where
T: UnsafeUnpin,
impl<T> UnwindSafe for ShouldBe<T>where
T: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Comparable<K> for Q
impl<Q, K> Comparable<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.