Skip to main content

verit_core/
error.rs

1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum Error {
5    /// Message does not start with the "VRT" magic family at all — not a
6    /// Veritate message.
7    BadMagic,
8    /// A Veritate message ("VRT" prefix) whose version byte this decoder does
9    /// not implement. `found` is the version digit in the magic; `supported`
10    /// is what this build understands. Guarantees a vN message can never be
11    /// silently misread as vM.
12    UnsupportedVersion { found: u8, supported: u8 },
13    /// The fixed header is structurally invalid for this version: an unknown
14    /// flag bit, or a reserved field that is not zero.
15    MalformedHeader(&'static str),
16    /// Buffer too short for the fixed header or a declared region.
17    Truncated,
18    /// An offset or length points outside the message buffer.
19    OutOfBounds,
20    /// List element index past the end of the list.
21    IndexOutOfBounds,
22    /// A string field holds invalid UTF-8.
23    BadUtf8,
24    /// The (inline) schema bytes are malformed or non-canonical.
25    BadSchema(String),
26    /// A value's type does not match the schema field type (write path),
27    /// or a typed getter was used on a differently-typed field (read path).
28    TypeMismatch { expected: String, got: String },
29    /// Writer and reader schema cannot be resolved (e.g. int narrowing).
30    Incompatible(String),
31    /// A value referenced a field ID that is not in the schema.
32    UnknownFieldId(u16),
33    /// The same field ID appeared twice in one struct value.
34    DuplicateField(u16),
35    /// The same key appeared twice in one map value.
36    DuplicateMapKey,
37    /// A `union` value or wire tag selected a variant index that does not exist.
38    BadUnionTag(u32),
39    /// A dense struct was encoded without one of its (mandatory) fields.
40    MissingField(u16),
41    /// The message's schema id does not match the resolver's writer schema
42    /// (or the inline schema bytes hash to something else).
43    SchemaIdMismatch { message: u128, expected: u128 },
44    /// dump_json needs an inline schema but the message was hash-only.
45    NoInlineSchema,
46    /// Recursion (nested structs/lists) exceeded the safety depth limit — the
47    /// message may contain an offset cycle or hostile nesting.
48    DepthLimitExceeded,
49    /// A bounded read exhausted its traversal budget: the message followed
50    /// offsets that would touch more bytes than the budget allows (an
51    /// amplification guard for untrusted input — see `Budget` / the wire spec §5.2).
52    TraversalBudgetExceeded,
53    /// Message would exceed the 4 GiB u32-offset limit.
54    MessageTooLarge,
55    /// A `.vertc` container file is malformed: bad magic/version, an index
56    /// pointing out of bounds, a misaligned or overlapping record, etc.
57    BadContainer(&'static str),
58    /// A `.verit` file is structurally malformed: bad magic or version, a
59    /// nonzero reserved field, an index entry pointing outside the record
60    /// region, a misaligned offset, and so on.
61    BadFile(&'static str),
62    /// The `.verit` file sets a bit in `required_features` that this build does
63    /// not implement, so it cannot be read correctly (File Format
64    /// Specification §3.1). Unknown *optional* feature bits are ignored, not
65    /// reported here.
66    UnsupportedFileFeature(u32),
67    /// No valid footer was found anywhere in a `.verit` file, so no committed
68    /// state is recoverable — the file was destroyed, not merely torn.
69    NoValidFooter,
70    /// A `.verit` file's index references a schema id its schema section does
71    /// not contain, so the file is not self-contained.
72    MissingSchema(u128),
73    /// Another writer already holds this file's advisory lock. The format
74    /// allows one writer and many readers (File Format Specification §7.3);
75    /// concurrent writers are undefined, so this refuses rather than racing.
76    /// Carries the lock file's path — delete it by hand if a previous writer
77    /// was killed.
78    AlreadyLocked(String),
79    /// A record's bytes do not match the CRC-32 the file stored for it — bit
80    /// rot, or a tampered record. Only reachable on a file written with
81    /// per-record checksums (`OPT_RECORD_CRC`). Names the record's stable id
82    /// rather than its position, since positions shift.
83    ChecksumMismatch {
84        /// The record's stable id.
85        id: u64,
86        /// What the file says the CRC should be.
87        expected: u32,
88        /// What the bytes actually hash to.
89        found: u32,
90    },
91    /// The underlying I/O operation failed. Carried as a string so `Error`
92    /// stays `Clone + PartialEq` (`std::io::Error` is neither).
93    Io(String),
94    /// Internal invariant violation (a bug in this library).
95    Internal(&'static str),
96}
97
98impl fmt::Display for Error {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        match self {
101            Error::BadMagic => write!(f, "not a Veritate message (bad magic)"),
102            Error::UnsupportedVersion { found, supported } => write!(
103                f,
104                "unsupported Veritate wire version: message is v{}, this build implements v{}",
105                *found as char, *supported as char
106            ),
107            Error::MalformedHeader(s) => write!(f, "malformed message header: {s}"),
108            Error::Truncated => write!(f, "message truncated"),
109            Error::OutOfBounds => write!(f, "offset out of bounds"),
110            Error::IndexOutOfBounds => write!(f, "list index out of bounds"),
111            Error::BadUtf8 => write!(f, "string field is not valid UTF-8"),
112            Error::BadSchema(s) => write!(f, "bad schema: {s}"),
113            Error::TypeMismatch { expected, got } => {
114                write!(f, "type mismatch: expected {expected}, got {got}")
115            }
116            Error::Incompatible(s) => write!(f, "schemas incompatible: {s}"),
117            Error::UnknownFieldId(id) => write!(f, "field id {id} not in schema"),
118            Error::DuplicateField(id) => write!(f, "field id {id} set twice"),
119            Error::DuplicateMapKey => write!(f, "the same map key was set twice"),
120            Error::BadUnionTag(t) => write!(f, "union tag {t} is not a valid variant index"),
121            Error::MissingField(id) => {
122                write!(f, "dense struct requires field id {id}, which was not set")
123            }
124            Error::SchemaIdMismatch { message, expected } => write!(
125                f,
126                "schema id mismatch: message has {message:#034x}, expected {expected:#034x}"
127            ),
128            Error::NoInlineSchema => write!(f, "message carries no inline schema"),
129            Error::DepthLimitExceeded => {
130                write!(f, "nesting depth limit exceeded (possible offset cycle)")
131            }
132            Error::TraversalBudgetExceeded => {
133                write!(
134                    f,
135                    "traversal budget exceeded (possible amplification attack)"
136                )
137            }
138            Error::MessageTooLarge => write!(f, "message exceeds 4 GiB limit"),
139            Error::BadContainer(s) => write!(f, "bad container: {s}"),
140            Error::BadFile(s) => write!(f, "bad .verit file: {s}"),
141            Error::UnsupportedFileFeature(bits) => write!(
142                f,
143                ".verit file requires feature bits {bits:#010x} this build does not implement"
144            ),
145            Error::NoValidFooter => {
146                write!(f, "no valid footer in .verit file (no recoverable commit)")
147            }
148            Error::MissingSchema(id) => write!(
149                f,
150                ".verit file index references schema {id:#034x}, which its schema section does not contain"
151            ),
152            Error::ChecksumMismatch { id, expected, found } => write!(
153                f,
154                "record {id} fails its checksum: file says {expected:#010x}, bytes hash to {found:#010x}"
155            ),
156            Error::AlreadyLocked(p) => write!(
157                f,
158                "another writer holds this .verit file (lock: {p}); \
159                 one writer at a time, many readers"
160            ),
161            Error::Io(s) => write!(f, "i/o error: {s}"),
162            Error::Internal(s) => write!(f, "internal error: {s}"),
163        }
164    }
165}
166
167impl std::error::Error for Error {}
168
169impl From<std::io::Error> for Error {
170    fn from(e: std::io::Error) -> Error {
171        Error::Io(e.to_string())
172    }
173}
174
175pub type Result<T> = std::result::Result<T, Error>;