Skip to content

Commit 059e948

Browse files
miguelfrdeJohnTitor
authored andcommitted
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 059e948

File tree

5 files changed

+97
-66
lines changed

5 files changed

+97
-66
lines changed

src/errors.rs

+42-15
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,56 @@ 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("Invalid MAC address")]
37+
InvalidMACAddress,
38+
39+
#[error("Invalid IP address")]
40+
InvalidIPAddress,
41+
42+
#[error("Invalid string")]
43+
Utf8Error(#[from] std::string::FromUtf8Error),
44+
45+
#[error("Invalid u8")]
46+
InvalidU8,
47+
48+
#[error("Invalid u16")]
49+
InvalidU16,
50+
51+
#[error("Invalid u32")]
52+
InvalidU32,
53+
54+
#[error("Invalid u64")]
55+
InvalidU64,
56+
57+
#[error("Invalid u128")]
58+
InvalidU128,
59+
60+
#[error("Invalid i32")]
61+
InvalidI32,
62+
63+
#[error("Invalid {name}: length {len} < {buffer_len}")]
64+
InvalidBufferLength {
65+
name: &'static str,
66+
len: usize,
67+
buffer_len: usize,
68+
},
69+
70+
#[error(transparent)]
71+
Nla(#[from] crate::nla::NlaError),
72+
73+
#[error(transparent)]
74+
Other(#[from] anyhow::Error),
3875
}
3976

4077
impl From<&'static str> for DecodeError {
4178
fn from(msg: &'static str) -> Self {
42-
DecodeError {
43-
inner: anyhow!(msg),
44-
}
79+
DecodeError::Other(anyhow!(msg))
4580
}
4681
}
4782

4883
impl From<String> for DecodeError {
4984
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 }
85+
DecodeError::Other(anyhow!(msg))
5986
}
6087
}

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::InvalidBufferLength {
196+
name: stringify!($name),
197+
len,
198+
buffer_len: $buffer_len,
199+
})
204200
} else {
205201
Ok(())
206202
}

src/nla.rs

+32-25
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,33 +64,26 @@ 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 }.into())
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-
)
78+
Err(NlaError::LengthMismatch {
79+
buffer_len: len,
80+
nla_len: self.length(),
81+
}
7582
.into())
7683
} 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-
)
84+
Err(NlaError::InvalidLength {
85+
nla_len: self.length(),
86+
}
8287
.into())
8388
} else {
8489
Ok(())
@@ -204,7 +209,9 @@ impl Nla for DefaultNla {
204209
impl<'buffer, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'buffer T>>
205210
for DefaultNla
206211
{
207-
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, DecodeError> {
212+
type Error = DecodeError;
213+
214+
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, Self::Error> {
208215
let mut kind = buf.kind();
209216

210217
if buf.network_byte_order_flag() {
@@ -314,7 +321,7 @@ impl<T> NlasIterator<T> {
314321
impl<'buffer, T: AsRef<[u8]> + ?Sized + 'buffer> Iterator
315322
for NlasIterator<&'buffer T>
316323
{
317-
type Item = Result<NlaBuffer<&'buffer [u8]>, DecodeError>;
324+
type Item = Result<NlaBuffer<&'buffer [u8]>, NlaError>;
318325

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

src/parsers.rs

+12-13
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ 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);
1615
}
1716
let mut address: [u8; 6] = [0; 6];
1817
for (i, byte) in payload.iter().enumerate() {
@@ -23,7 +22,7 @@ pub fn parse_mac(payload: &[u8]) -> Result<[u8; 6], DecodeError> {
2322

2423
pub fn parse_ipv6(payload: &[u8]) -> Result<[u8; 16], DecodeError> {
2524
if payload.len() != 16 {
26-
return Err(format!("invalid IPv6 address: {payload:?}").into());
25+
return Err(DecodeError::InvalidIPAddress);
2726
}
2827
let mut address: [u8; 16] = [0; 16];
2928
for (i, byte) in payload.iter().enumerate() {
@@ -57,7 +56,7 @@ pub fn parse_ip(payload: &[u8]) -> Result<IpAddr, DecodeError> {
5756
payload[15],
5857
])
5958
.into()),
60-
_ => Err(format!("invalid IPv6 address: {payload:?}").into()),
59+
_ => Err(DecodeError::InvalidIPAddress),
6160
}
6261
}
6362

@@ -71,62 +70,62 @@ pub fn parse_string(payload: &[u8]) -> Result<String, DecodeError> {
7170
} else {
7271
&payload[..payload.len()]
7372
};
74-
let s = String::from_utf8(slice.to_vec()).context("invalid string")?;
73+
let s = String::from_utf8(slice.to_vec())?;
7574
Ok(s)
7675
}
7776

7877
pub fn parse_u8(payload: &[u8]) -> Result<u8, DecodeError> {
7978
if payload.len() != 1 {
80-
return Err(format!("invalid u8: {payload:?}").into());
79+
return Err(DecodeError::InvalidU8);
8180
}
8281
Ok(payload[0])
8382
}
8483

8584
pub fn parse_u32(payload: &[u8]) -> Result<u32, DecodeError> {
8685
if payload.len() != size_of::<u32>() {
87-
return Err(format!("invalid u32: {payload:?}").into());
86+
return Err(DecodeError::InvalidU32);
8887
}
8988
Ok(NativeEndian::read_u32(payload))
9089
}
9190

9291
pub fn parse_u64(payload: &[u8]) -> Result<u64, DecodeError> {
9392
if payload.len() != size_of::<u64>() {
94-
return Err(format!("invalid u64: {payload:?}").into());
93+
return Err(DecodeError::InvalidU64);
9594
}
9695
Ok(NativeEndian::read_u64(payload))
9796
}
9897

9998
pub fn parse_u128(payload: &[u8]) -> Result<u128, DecodeError> {
10099
if payload.len() != size_of::<u128>() {
101-
return Err(format!("invalid u128: {payload:?}").into());
100+
return Err(DecodeError::InvalidU128);
102101
}
103102
Ok(NativeEndian::read_u128(payload))
104103
}
105104

106105
pub fn parse_u16(payload: &[u8]) -> Result<u16, DecodeError> {
107106
if payload.len() != size_of::<u16>() {
108-
return Err(format!("invalid u16: {payload:?}").into());
107+
return Err(DecodeError::InvalidU16);
109108
}
110109
Ok(NativeEndian::read_u16(payload))
111110
}
112111

113112
pub fn parse_i32(payload: &[u8]) -> Result<i32, DecodeError> {
114113
if payload.len() != 4 {
115-
return Err(format!("invalid u32: {payload:?}").into());
114+
return Err(DecodeError::InvalidI32);
116115
}
117116
Ok(NativeEndian::read_i32(payload))
118117
}
119118

120119
pub fn parse_u16_be(payload: &[u8]) -> Result<u16, DecodeError> {
121120
if payload.len() != size_of::<u16>() {
122-
return Err(format!("invalid u16: {payload:?}").into());
121+
return Err(DecodeError::InvalidU16);
123122
}
124123
Ok(BigEndian::read_u16(payload))
125124
}
126125

127126
pub fn parse_u32_be(payload: &[u8]) -> Result<u32, DecodeError> {
128127
if payload.len() != size_of::<u32>() {
129-
return Err(format!("invalid u32: {payload:?}").into());
128+
return Err(DecodeError::InvalidU32);
130129
}
131130
Ok(BigEndian::read_u32(payload))
132131
}

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)