1use std::fmt;
16use std::result::Result as StdResult;
17use std::str::Utf8Error;
18use thiserror::Error;
19
20pub type Result<T> = StdResult<T, Error>;
21
22#[derive(Error, Debug)]
23pub enum Error {
24 #[error("decode error: {0}")]
25 Decode(#[from] DecodeError),
26 #[error("encode error: {0}")]
27 Encode(#[from] EncodeError),
28}
29
30#[derive(Error, Debug, Clone, PartialEq)]
31pub enum DecodeError {
32 #[error("unexpected end of input")]
33 UnexpectedEof,
34 #[error("invalid tag: {0}")]
35 InvalidTag(u8),
36 #[error("invalid version: expected {expected}, got {actual}")]
37 InvalidVersion { expected: u8, actual: u8 },
38 #[error("invalid UTF-8 in atom: {0}")]
39 InvalidUtf8(String),
40 #[error("atom too large: {size} bytes (max {max})")]
41 AtomTooLarge { size: usize, max: usize },
42 #[error("list too large: {size} elements (max {max})")]
43 ListTooLarge { size: usize, max: usize },
44 #[error("tuple too large: {size} elements (max {max})")]
45 TupleTooLarge { size: usize, max: usize },
46 #[error("map too large: {size} entries (max {max})")]
47 MapTooLarge { size: usize, max: usize },
48 #[error("binary too large: {size} bytes (max {max})")]
49 BinaryTooLarge { size: usize, max: usize },
50 #[error("invalid list structure")]
51 InvalidList,
52 #[error("invalid map structure")]
53 InvalidMap,
54 #[error("unsupported term type: {0}")]
55 UnsupportedType(String),
56 #[error("buffer too small: need {needed} bytes, have {available}")]
57 BufferTooSmall { needed: usize, available: usize },
58 #[error("invalid format: {0}")]
59 InvalidFormat(String),
60 #[error("trailing data: {0} bytes after term")]
61 TrailingData(usize),
62 #[error("invalid PID format: {0}")]
63 InvalidPidFormat(String),
64}
65
66#[derive(Debug, Clone, PartialEq)]
67pub struct ParsingContext {
68 pub byte_offset: usize,
69 pub path: Vec<PathSegment>,
70}
71
72#[derive(Debug, Clone, PartialEq)]
73pub enum PathSegment {
74 TupleElement(usize),
75 ListElement(usize),
76 MapKey,
77 MapValue(String),
78 ImproperListTail,
79 FunFreeVar(usize),
80}
81
82impl ParsingContext {
83 pub fn new() -> Self {
84 ParsingContext {
85 byte_offset: 0,
86 path: Vec::new(),
87 }
88 }
89
90 pub fn with_offset(offset: usize) -> Self {
91 ParsingContext {
92 byte_offset: offset,
93 path: Vec::new(),
94 }
95 }
96
97 pub fn push(&mut self, segment: PathSegment) {
98 self.path.push(segment);
99 }
100
101 pub fn pop(&mut self) {
102 self.path.pop();
103 }
104
105 pub fn display_path(&self) -> String {
106 if self.path.is_empty() {
107 return "root".to_string();
108 }
109
110 let mut result = String::from("root");
111 for segment in &self.path {
112 match segment {
113 PathSegment::TupleElement(i) => result.push_str(&format!("[{}]", i)),
114 PathSegment::ListElement(i) => result.push_str(&format!("[{}]", i)),
115 PathSegment::MapKey => result.push_str(".key"),
116 PathSegment::MapValue(k) => result.push_str(&format!(".{}", k)),
117 PathSegment::ImproperListTail => result.push_str(".tail"),
118 PathSegment::FunFreeVar(i) => result.push_str(&format!(".free_var[{}]", i)),
119 }
120 }
121 result
122 }
123}
124
125impl Default for ParsingContext {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131#[derive(Error, Debug, Clone, PartialEq)]
132pub struct ContextualDecodeError {
133 pub error: DecodeError,
134 pub context: ParsingContext,
135}
136
137impl fmt::Display for ContextualDecodeError {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 write!(
140 f,
141 "{} at byte offset {} in path {}",
142 self.error,
143 self.context.byte_offset,
144 self.context.display_path()
145 )
146 }
147}
148
149impl ContextualDecodeError {
150 pub fn new(error: DecodeError, context: ParsingContext) -> Self {
151 ContextualDecodeError { error, context }
152 }
153}
154
155#[derive(Error, Debug)]
156pub enum EncodeError {
157 #[error("atom too large: {size} bytes (max 65535)")]
158 AtomTooLarge { size: usize },
159 #[error("string too large: {size} bytes")]
160 StringTooLarge { size: usize },
161 #[error("list too large: {size} elements")]
162 ListTooLarge { size: usize },
163 #[error("map too large: {size} entries")]
164 MapTooLarge { size: usize },
165 #[error("binary too large: {size} bytes")]
166 BinaryTooLarge { size: usize },
167 #[error("tuple too large: {size} elements (max 4294967295)")]
168 TupleTooLarge { size: usize },
169 #[error("reference has too many IDs: {size} (max 65535)")]
170 ReferenceTooLarge { size: usize },
171 #[error("too many atoms for DIST_HEADER: {count} (max 255)")]
172 TooManyAtoms { count: usize },
173 #[error("I/O error: {0}")]
174 IoError(#[from] std::io::Error),
175 #[error("buffer overflow")]
176 BufferOverflow,
177}
178
179#[derive(Error, Debug, Clone, PartialEq)]
180pub enum TermConversionError {
181 #[error("expected {expected}, got {actual}")]
182 WrongType {
183 expected: &'static str,
184 actual: &'static str,
185 },
186 #[error("value out of range for target type")]
187 OutOfRange,
188}
189
190impl From<Utf8Error> for DecodeError {
191 fn from(e: Utf8Error) -> Self {
192 DecodeError::InvalidUtf8(e.to_string())
193 }
194}