Skip to main content

offline_protocol_data/
error.rs

1//! Errors raised by the data layer.
2
3use thiserror::Error;
4
5/// Result alias for data-layer operations.
6pub type DataResult<T> = std::result::Result<T, DataError>;
7
8/// A failure inside the replicated-document layer.
9///
10/// Every variant is either a caller mistake (a bad key, an out-of-range
11/// position, a document over the cap) or a permanent corruption verdict.
12/// Transient conditions do not appear here: persistence retries live above
13/// this crate, on the SDK's storage seam.
14#[derive(Debug, Error)]
15#[non_exhaustive]
16pub enum DataError {
17    /// A document, snapshot, or delta could not be decoded.
18    ///
19    /// The bytes reaching this crate always come back out of a sealed
20    /// protocol-state record, so an AEAD tag has already vouched for them.
21    /// This variant therefore means the engine rejected its own output:
22    /// treat the document as permanently unreadable rather than retrying.
23    #[error("document data is corrupt: {0}")]
24    Corrupt(String),
25
26    /// A document handle was used after a decode panic poisoned it.
27    ///
28    /// Held separate from [`DataError::Corrupt`] because the failure is
29    /// sticky: every later call on the same handle answers this, and the
30    /// only recovery is to drop the handle and re-open from storage.
31    #[error("document handle is poisoned by an earlier decode failure")]
32    Poisoned,
33
34    /// The compacted document would exceed [`crate::MAX_DOC_BYTES`].
35    ///
36    /// Raised at commit, never as a silent truncation: an application that
37    /// keeps typing into a full document has to hear about it while the
38    /// text is still on screen.
39    #[error("document is {actual} bytes compacted, over the {limit} byte cap")]
40    DocTooLarge {
41        /// Size of the compacted encoding that breached the cap.
42        actual: usize,
43        /// The cap in force, [`crate::MAX_DOC_BYTES`].
44        limit: usize,
45    },
46
47    /// A single value is larger than [`crate::MAX_VALUE_BYTES`].
48    ///
49    /// Refused at the operation rather than at commit. A value this size can
50    /// never fit in a document, and discovering that at commit means the
51    /// document's next delta record is unwritable and stays unwritable,
52    /// because a refused write is re-exported (larger) by the commit after it.
53    #[error("value is {actual} bytes, over the {limit} byte limit")]
54    ValueTooLarge {
55        /// Size of the rejected value.
56        actual: usize,
57        /// The limit in force, [`crate::MAX_VALUE_BYTES`].
58        limit: usize,
59    },
60
61    /// A collection or document name is empty, over-long, or uses characters
62    /// outside the accepted set.
63    #[error("invalid name {name:?}: {reason}")]
64    InvalidName {
65        /// The rejected name.
66        name: String,
67        /// Why it was rejected.
68        reason: &'static str,
69    },
70
71    /// An attachment reference is malformed.
72    ///
73    /// Separate from [`DataError::InvalidName`] because an attachment hash
74    /// is not a name the caller chose, it is the address of the bytes. A
75    /// reference whose hash is not a SHA-256 can never be fetched, and a
76    /// reference that cannot be fetched is worse than no reference at all:
77    /// it replicates to every member of the space, and every one of them
78    /// sees an attachment that will never arrive.
79    #[error("invalid attachment: {reason}")]
80    InvalidAttachment {
81        /// Why it was rejected.
82        reason: &'static str,
83    },
84
85    /// A list or text position is outside the current length.
86    #[error("position {position} is out of range for length {length}")]
87    OutOfRange {
88        /// The position the caller asked for.
89        position: usize,
90        /// The current length of the collection.
91        length: usize,
92    },
93
94    /// The engine refused an otherwise well-formed operation.
95    #[error("data operation failed: {0}")]
96    Engine(String),
97}