ts_wire/error.rs
1// Copyright (c) 2026 LightSeek Foundation
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Decode errors.
22
23/// A message that does not match the schema, or is not valid msgpack.
24#[derive(Debug, thiserror::Error)]
25pub enum Error {
26 /// The input ended inside a value.
27 #[error("input truncated at offset {offset}: {needed} more bytes needed")]
28 Truncated {
29 /// Byte offset in the primary frame where the value started.
30 offset: usize,
31 /// Bytes missing past the end of the frame.
32 needed: usize,
33 },
34 /// A value of a different msgpack type than the schema allows.
35 #[error("expected {expected}, found {found} at offset {offset}")]
36 Type {
37 /// What the schema allows here.
38 expected: &'static str,
39 /// The msgpack type found.
40 found: &'static str,
41 /// Byte offset in the primary frame.
42 offset: usize,
43 },
44 /// An integer outside the range of the schema's type.
45 #[error("integer {value} out of range for {target}")]
46 IntRange {
47 /// The decoded integer.
48 value: i128,
49 /// The schema type.
50 target: &'static str,
51 },
52 /// A string that is not valid UTF-8.
53 #[error("invalid UTF-8 string at offset {offset}")]
54 Utf8 {
55 /// Byte offset in the primary frame.
56 offset: usize,
57 },
58 /// A tagged struct whose tag names a different message type.
59 #[error("wrong message tag: expected `{expected}`, found `{found}`")]
60 Tag {
61 /// The tag of the type being decoded.
62 expected: &'static str,
63 /// The tag on the wire.
64 found: String,
65 },
66 /// A struct without a value for a field that has no default.
67 #[error("missing required field `{0}`")]
68 MissingField(&'static str),
69 /// Bytes left in the primary frame after the top-level value.
70 #[error("{0} trailing bytes after the message")]
71 TrailingBytes(usize),
72 /// A value the schema rejects for a reason other than its type.
73 #[error("{0}")]
74 Invalid(String),
75 /// An error inside a named field, kept with the field name for context.
76 #[error("field `{field}`: {source}")]
77 Field {
78 /// The field being decoded.
79 field: &'static str,
80 /// The underlying error.
81 #[source]
82 source: Box<Error>,
83 },
84}
85
86impl Error {
87 /// Attach the name of the field being decoded.
88 pub fn in_field(self, field: &'static str) -> Self {
89 Error::Field {
90 field,
91 source: Box::new(self),
92 }
93 }
94}
95
96/// Result alias for decoding.
97pub type Result<T> = std::result::Result<T, Error>;