airmash_protocol/v5/
error.rs1use std::fmt::{self, Display};
2
3pub trait ErrorExt {
4 fn with_context(self, field: &'static str) -> Self;
5}
6
7#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
8pub enum ErrorKind {
9 EndOfBuffer,
10 InvalidEnumValue,
11 ArraySizeTooLarge,
12 UnexpectedDataRemaining,
13}
14
15#[derive(Clone, Debug)]
16pub struct Error {
17 kind: ErrorKind,
18 context: Vec<&'static str>,
19}
20
21impl Error {
22 pub fn new(kind: ErrorKind) -> Self {
23 Self {
24 kind,
25 context: Vec::new(),
26 }
27 }
28
29 pub fn kind(&self) -> ErrorKind {
30 self.kind
31 }
32
33 pub fn context(&self) -> &[&'static str] {
34 &self.context[..]
35 }
36
37 fn description(&self) -> &str {
38 match self.kind() {
39 ErrorKind::EndOfBuffer => "reached end of buffer",
40 ErrorKind::InvalidEnumValue => "invalid enum value",
41 ErrorKind::ArraySizeTooLarge => "array size too large for type",
42 ErrorKind::UnexpectedDataRemaining => "data left over after deserialization finished",
43 }
44 }
45}
46
47impl std::error::Error for Error {
48 fn description(&self) -> &str {
49 Self::description(self)
50 }
51}
52
53impl Display for Error {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 writeln!(f, "failed to parse: {}", self.description())?;
56 writeln!(f, "Field stack:")?;
57
58 for field in self.context().iter().copied().rev() {
59 writeln!(f, " - {}", field)?;
60 }
61
62 Ok(())
63 }
64}
65
66impl ErrorExt for Error {
67 fn with_context(mut self, field: &'static str) -> Self {
68 self.context.push(field);
69 self
70 }
71}
72
73impl<T> ErrorExt for Result<T, Error> {
74 fn with_context(self, field: &'static str) -> Self {
75 self.map_err(|e| e.with_context(field))
76 }
77}