Skip to content

Commit e60c548

Browse files
committed
Use thiserror Errors instead of relying on anyhow
This improves matching on particular errors when we need to handle different conditions downstream. It's still possible to convert a anyhow::Error to a DecodeError in this change, but every other error this crate expsoes is now in a variant.
1 parent 97c4a4b commit e60c548

File tree

5 files changed

+123
-73
lines changed

5 files changed

+123
-73
lines changed

src/errors.rs

+37-17
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,49 @@ impl From<anyhow::Error> for EncodeError {
3232
}
3333

3434
#[derive(Debug, Error)]
35-
#[error("Decode error occurred: {inner}")]
36-
pub struct DecodeError {
37-
inner: anyhow::Error,
35+
pub enum DecodeError {
36+
#[error(
37+
"Invalid MAC address. Expected 6 bytes, received {received} bytes"
38+
)]
39+
InvalidMACAddress { received: usize },
40+
41+
#[error(
42+
"Invalid IP address. Expected 4 or 16 bytes, received {received} bytes"
43+
)]
44+
InvalidIPAddress { received: usize },
45+
46+
#[error("Invalid string")]
47+
Utf8Error(#[from] std::string::FromUtf8Error),
48+
49+
#[error(
50+
"Invalid number. Expected {expected} bytes, received {received} bytes"
51+
)]
52+
InvalidNumber { expected: usize, received: usize },
53+
54+
#[error("Invalid buffer {name}. Expected at least {minimum_length} bytes, received {received} bytes")]
55+
InvalidBuffer {
56+
name: &'static str,
57+
received: usize,
58+
minimum_length: usize,
59+
},
60+
61+
#[error(transparent)]
62+
Nla(#[from] crate::nla::NlaError),
63+
64+
#[error(transparent)]
65+
Other(#[from] Box<dyn std::error::Error>),
3866
}
3967

40-
impl From<&'static str> for DecodeError {
41-
fn from(msg: &'static str) -> Self {
42-
DecodeError {
43-
inner: anyhow!(msg),
44-
}
68+
impl From<&str> for DecodeError {
69+
fn from(msg: &str) -> Self {
70+
let error: Box<dyn std::error::Error> = msg.to_string().into();
71+
DecodeError::Other(error)
4572
}
4673
}
4774

4875
impl From<String> for DecodeError {
4976
fn from(msg: String) -> Self {
50-
DecodeError {
51-
inner: anyhow!(msg),
52-
}
53-
}
54-
}
55-
56-
impl From<anyhow::Error> for DecodeError {
57-
fn from(inner: anyhow::Error) -> DecodeError {
58-
DecodeError { inner }
77+
let error: Box<dyn std::error::Error> = msg.into();
78+
DecodeError::Other(error)
5979
}
6080
}

src/macros.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,11 @@ macro_rules! buffer_check_length {
192192
fn check_buffer_length(&self) -> Result<(), DecodeError> {
193193
let len = self.buffer.as_ref().len();
194194
if len < $buffer_len {
195-
Err(format!(
196-
concat!(
197-
"invalid ",
198-
stringify!($name),
199-
": length {} < {}"
200-
),
201-
len, $buffer_len
202-
)
203-
.into())
195+
Err(DecodeError::InvalidBuffer {
196+
name: stringify!($name),
197+
received: len,
198+
minimum_length: $buffer_len,
199+
})
204200
} else {
205201
Ok(())
206202
}

src/nla.rs

+35-30
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
// SPDX-License-Identifier: MIT
22

3-
use core::ops::Range;
4-
5-
use anyhow::Context;
6-
use byteorder::{ByteOrder, NativeEndian};
7-
83
use crate::{
94
traits::{Emitable, Parseable},
105
DecodeError,
116
};
7+
use byteorder::{ByteOrder, NativeEndian};
8+
use core::ops::Range;
9+
use thiserror::Error;
1210

1311
/// Represent a multi-bytes field with a fixed size in a packet
1412
type Field = Range<usize>;
@@ -25,6 +23,20 @@ pub const NLA_ALIGNTO: usize = 4;
2523
/// NlA(RTA) header size. (unsigned short rta_len) + (unsigned short rta_type)
2624
pub const NLA_HEADER_SIZE: usize = 4;
2725

26+
#[derive(Debug, Error)]
27+
pub enum NlaError {
28+
#[error("buffer has length {buffer_len}, but an NLA header is {} bytes", TYPE.end)]
29+
BufferTooSmall { buffer_len: usize },
30+
31+
#[error("buffer has length: {buffer_len}, but the NLA is {nla_len} bytes")]
32+
LengthMismatch { buffer_len: usize, nla_len: u16 },
33+
34+
#[error(
35+
"NLA has invalid length: {nla_len} (should be at least {} bytes", TYPE.end
36+
)]
37+
InvalidLength { nla_len: u16 },
38+
}
39+
2840
#[macro_export]
2941
macro_rules! nla_align {
3042
($len: expr) => {
@@ -52,34 +64,25 @@ impl<T: AsRef<[u8]>> NlaBuffer<T> {
5264
NlaBuffer { buffer }
5365
}
5466

55-
pub fn new_checked(buffer: T) -> Result<NlaBuffer<T>, DecodeError> {
67+
pub fn new_checked(buffer: T) -> Result<NlaBuffer<T>, NlaError> {
5668
let buffer = Self::new(buffer);
57-
buffer.check_buffer_length().context("invalid NLA buffer")?;
69+
buffer.check_buffer_length()?;
5870
Ok(buffer)
5971
}
6072

61-
pub fn check_buffer_length(&self) -> Result<(), DecodeError> {
73+
pub fn check_buffer_length(&self) -> Result<(), NlaError> {
6274
let len = self.buffer.as_ref().len();
6375
if len < TYPE.end {
64-
Err(format!(
65-
"buffer has length {}, but an NLA header is {} bytes",
66-
len, TYPE.end
67-
)
68-
.into())
76+
Err(NlaError::BufferTooSmall { buffer_len: len })
6977
} else if len < self.length() as usize {
70-
Err(format!(
71-
"buffer has length: {}, but the NLA is {} bytes",
72-
len,
73-
self.length()
74-
)
75-
.into())
78+
Err(NlaError::LengthMismatch {
79+
buffer_len: len,
80+
nla_len: self.length(),
81+
})
7682
} else if (self.length() as usize) < TYPE.end {
77-
Err(format!(
78-
"NLA has invalid length: {} (should be at least {} bytes",
79-
self.length(),
80-
TYPE.end,
81-
)
82-
.into())
83+
Err(NlaError::InvalidLength {
84+
nla_len: self.length(),
85+
})
8386
} else {
8487
Ok(())
8588
}
@@ -162,14 +165,14 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> NlaBuffer<T> {
162165
}
163166
}
164167

165-
impl<'buffer, T: AsRef<[u8]> + ?Sized> NlaBuffer<&'buffer T> {
168+
impl<T: AsRef<[u8]> + ?Sized> NlaBuffer<&T> {
166169
/// Return the `value` field
167170
pub fn value(&self) -> &[u8] {
168171
&self.buffer.as_ref()[VALUE(self.value_length())]
169172
}
170173
}
171174

172-
impl<'buffer, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NlaBuffer<&'buffer mut T> {
175+
impl<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NlaBuffer<&mut T> {
173176
/// Return the `value` field
174177
pub fn value_mut(&mut self) -> &mut [u8] {
175178
let length = VALUE(self.value_length());
@@ -204,7 +207,9 @@ impl Nla for DefaultNla {
204207
impl<'buffer, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'buffer T>>
205208
for DefaultNla
206209
{
207-
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, DecodeError> {
210+
type Error = DecodeError;
211+
212+
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, Self::Error> {
208213
let mut kind = buf.kind();
209214

210215
if buf.network_byte_order_flag() {
@@ -273,7 +278,7 @@ impl<T: Nla> Emitable for T {
273278
// The reason this does not work today is because it conflicts with
274279
//
275280
// impl<T: Nla> Emitable for T { ... }
276-
impl<'a, T: Nla> Emitable for &'a [T] {
281+
impl<T: Nla> Emitable for &[T] {
277282
fn buffer_len(&self) -> usize {
278283
self.iter().fold(0, |acc, nla| {
279284
assert_eq!(nla.buffer_len() % NLA_ALIGNTO, 0);
@@ -314,7 +319,7 @@ impl<T> NlasIterator<T> {
314319
impl<'buffer, T: AsRef<[u8]> + ?Sized + 'buffer> Iterator
315320
for NlasIterator<&'buffer T>
316321
{
317-
type Item = Result<NlaBuffer<&'buffer [u8]>, DecodeError>;
322+
type Item = Result<NlaBuffer<&'buffer [u8]>, NlaError>;
318323

319324
fn next(&mut self) -> Option<Self::Item> {
320325
if self.position >= self.buffer.as_ref().len() {

src/parsers.rs

+40-13
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ use std::{
55
net::{IpAddr, Ipv4Addr, Ipv6Addr},
66
};
77

8-
use anyhow::Context;
98
use byteorder::{BigEndian, ByteOrder, NativeEndian};
109

1110
use crate::DecodeError;
1211

1312
pub fn parse_mac(payload: &[u8]) -> Result<[u8; 6], DecodeError> {
1413
if payload.len() != 6 {
15-
return Err(format!("invalid MAC address: {payload:?}").into());
14+
return Err(DecodeError::InvalidMACAddress {
15+
received: payload.len(),
16+
});
1617
}
1718
let mut address: [u8; 6] = [0; 6];
1819
for (i, byte) in payload.iter().enumerate() {
@@ -23,7 +24,9 @@ pub fn parse_mac(payload: &[u8]) -> Result<[u8; 6], DecodeError> {
2324

2425
pub fn parse_ipv6(payload: &[u8]) -> Result<[u8; 16], DecodeError> {
2526
if payload.len() != 16 {
26-
return Err(format!("invalid IPv6 address: {payload:?}").into());
27+
return Err(DecodeError::InvalidIPAddress {
28+
received: payload.len(),
29+
});
2730
}
2831
let mut address: [u8; 16] = [0; 16];
2932
for (i, byte) in payload.iter().enumerate() {
@@ -57,7 +60,7 @@ pub fn parse_ip(payload: &[u8]) -> Result<IpAddr, DecodeError> {
5760
payload[15],
5861
])
5962
.into()),
60-
_ => Err(format!("invalid IPv6 address: {payload:?}").into()),
63+
other => Err(DecodeError::InvalidIPAddress { received: other }),
6164
}
6265
}
6366

@@ -71,62 +74,86 @@ pub fn parse_string(payload: &[u8]) -> Result<String, DecodeError> {
7174
} else {
7275
&payload[..payload.len()]
7376
};
74-
let s = String::from_utf8(slice.to_vec()).context("invalid string")?;
77+
let s = String::from_utf8(slice.to_vec())?;
7578
Ok(s)
7679
}
7780

7881
pub fn parse_u8(payload: &[u8]) -> Result<u8, DecodeError> {
7982
if payload.len() != 1 {
80-
return Err(format!("invalid u8: {payload:?}").into());
83+
return Err(DecodeError::InvalidNumber {
84+
expected: 1,
85+
received: payload.len(),
86+
});
8187
}
8288
Ok(payload[0])
8389
}
8490

8591
pub fn parse_u32(payload: &[u8]) -> Result<u32, DecodeError> {
8692
if payload.len() != size_of::<u32>() {
87-
return Err(format!("invalid u32: {payload:?}").into());
93+
return Err(DecodeError::InvalidNumber {
94+
expected: size_of::<u32>(),
95+
received: payload.len(),
96+
});
8897
}
8998
Ok(NativeEndian::read_u32(payload))
9099
}
91100

92101
pub fn parse_u64(payload: &[u8]) -> Result<u64, DecodeError> {
93102
if payload.len() != size_of::<u64>() {
94-
return Err(format!("invalid u64: {payload:?}").into());
103+
return Err(DecodeError::InvalidNumber {
104+
expected: size_of::<u64>(),
105+
received: payload.len(),
106+
});
95107
}
96108
Ok(NativeEndian::read_u64(payload))
97109
}
98110

99111
pub fn parse_u128(payload: &[u8]) -> Result<u128, DecodeError> {
100112
if payload.len() != size_of::<u128>() {
101-
return Err(format!("invalid u128: {payload:?}").into());
113+
return Err(DecodeError::InvalidNumber {
114+
expected: size_of::<u128>(),
115+
received: payload.len(),
116+
});
102117
}
103118
Ok(NativeEndian::read_u128(payload))
104119
}
105120

106121
pub fn parse_u16(payload: &[u8]) -> Result<u16, DecodeError> {
107122
if payload.len() != size_of::<u16>() {
108-
return Err(format!("invalid u16: {payload:?}").into());
123+
return Err(DecodeError::InvalidNumber {
124+
expected: size_of::<u16>(),
125+
received: payload.len(),
126+
});
109127
}
110128
Ok(NativeEndian::read_u16(payload))
111129
}
112130

113131
pub fn parse_i32(payload: &[u8]) -> Result<i32, DecodeError> {
114132
if payload.len() != 4 {
115-
return Err(format!("invalid u32: {payload:?}").into());
133+
return Err(DecodeError::InvalidNumber {
134+
expected: 4,
135+
received: payload.len(),
136+
});
116137
}
117138
Ok(NativeEndian::read_i32(payload))
118139
}
119140

120141
pub fn parse_u16_be(payload: &[u8]) -> Result<u16, DecodeError> {
121142
if payload.len() != size_of::<u16>() {
122-
return Err(format!("invalid u16: {payload:?}").into());
143+
return Err(DecodeError::InvalidNumber {
144+
expected: size_of::<u16>(),
145+
received: payload.len(),
146+
});
123147
}
124148
Ok(BigEndian::read_u16(payload))
125149
}
126150

127151
pub fn parse_u32_be(payload: &[u8]) -> Result<u32, DecodeError> {
128152
if payload.len() != size_of::<u32>() {
129-
return Err(format!("invalid u32: {payload:?}").into());
153+
return Err(DecodeError::InvalidNumber {
154+
expected: size_of::<u32>(),
155+
received: payload.len(),
156+
});
130157
}
131158
Ok(BigEndian::read_u32(payload))
132159
}

src/traits.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// SPDX-License-Identifier: MIT
22

3-
use crate::DecodeError;
4-
53
/// A type that implements `Emitable` can be serialized.
64
pub trait Emitable {
75
/// Return the length of the serialized data.
@@ -26,8 +24,10 @@ where
2624
Self: Sized,
2725
T: ?Sized,
2826
{
27+
type Error;
28+
2929
/// Deserialize the current type.
30-
fn parse(buf: &T) -> Result<Self, DecodeError>;
30+
fn parse(buf: &T) -> Result<Self, Self::Error>;
3131
}
3232

3333
/// A `Parseable` type can be used to deserialize data from the type `T` for
@@ -37,6 +37,8 @@ where
3737
Self: Sized,
3838
T: ?Sized,
3939
{
40+
type Error;
41+
4042
/// Deserialize the current type.
41-
fn parse_with_param(buf: &T, params: P) -> Result<Self, DecodeError>;
43+
fn parse_with_param(buf: &T, params: P) -> Result<Self, Self::Error>;
4244
}

0 commit comments

Comments
 (0)