airmash_protocol/v5/
error.rs

1use 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}