1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use std::fmt;

use bytecheck::CheckBytes;
use rkyv::{
    check_archived_root,
    validation::{validators::DefaultValidator, CheckArchiveError},
    Archive,
};

/// Error that occurred during serialization.
#[derive(Debug)]
pub enum SerializeError<T> {
    /// The type failed to be serialized correctly.
    FailedToSerialize(String),

    /// The backing store was not big enough to fit the serialized version of the value.
    ///
    /// The original value that was given is returned, along with the minimum size that the backing
    /// store must be sized to hold the serialized value.  Providing a backing store that is larger
    /// than the given value is acceptable, but not necessary.
    BackingStoreTooSmall(T, usize),
}

/// Error that occurred during deserialization.
#[derive(Debug)]
pub enum DeserializeError {
    /// The data in the backing store does not represent the archive type as whole.
    ///
    /// This error is primarily indicative of not having enough data present, which is often a
    /// signal that the type represented by the bytes in the backing store and the incoming archive
    /// type are either entirely different, or that the structure of the type has changed: addition
    /// or removal of fields, reordering of fields, etc.
    ///
    /// The backing store that was given is returned, along with an error string that briefly
    /// describes the error in a more verbose fashion, suitable for debugging.
    InvalidStructure(String),

    /// Some of the data in the backing store cannot represent a particular field in the archive type.
    ///
    /// This would typically occur if the data read for a particular field could not specifically
    /// represent that type.  For example, a boolean is encoded as a single byte with a 0 or 1 as
    /// the value, so a value between 2 and 255 is inherently invalid for representing a boolean.
    ///
    /// This can be a subtle difference from `InvalidStructure`, but is primarily indicative of
    /// in-place data corruption, or data being overwritten by an outside process.
    ///
    /// The backing store that was given is returned, along with an error string that briefly
    /// describes the error in a more verbose fashion, suitable for debugging.
    InvalidData(String),
}

impl DeserializeError {
    /// Consumes this error and returns the stringified error reason.
    pub fn into_inner(self) -> String {
        match self {
            DeserializeError::InvalidData(s) => format!("invalid data: {s}"),
            DeserializeError::InvalidStructure(s) => format!("invalid structure: {s}"),
        }
    }
}

impl<T, C> From<CheckArchiveError<T, C>> for DeserializeError
where
    T: fmt::Display,
    C: fmt::Display,
{
    fn from(e: CheckArchiveError<T, C>) -> Self {
        match e {
            CheckArchiveError::ContextError(ce) => {
                DeserializeError::InvalidStructure(ce.to_string())
            }
            CheckArchiveError::CheckBytesError(cbe) => {
                DeserializeError::InvalidData(cbe.to_string())
            }
        }
    }
}

/// Tries to deserialize the given buffer as the archival type `T`.
///
/// The archived type is assumed to exist starting at index 0 of the buffer.  Additionally, the
/// archived value is checked for data conformance.
///
/// # Errors
///
/// If the buffer does not contained an archived `T`, or there was an issue with too little data, or
/// invalid values, etc, then an error variant will be emitted.  The error will describe the
/// high-level error, as well as provide a string with a more verbose explanation of the error.
pub fn try_as_archive<'a, T>(buf: &'a [u8]) -> Result<&'a T::Archived, DeserializeError>
where
    T: Archive,
    T::Archived: for<'b> CheckBytes<DefaultValidator<'b>>,
{
    debug_assert!(!buf.is_empty());
    check_archived_root::<T>(buf).map_err(Into::into)
}