Skip to content

Commit

Permalink
Partially seal error types. (#277)
Browse files Browse the repository at this point in the history
This PR looks at all the error types and splits them into meaningful error
information, which will be available as enum variants, and internals, which
will be hidden away behind opaque structs. For the former case, we will
still use enums directly rather than have error kind enums. These will not
be non-exhaustive since they should really cover all possible cases and
exhaustive matching should work.

This is a breaking change.
  • Loading branch information
partim authored Mar 21, 2024
1 parent b4436aa commit 1466e1e
Show file tree
Hide file tree
Showing 15 changed files with 398 additions and 323 deletions.
90 changes: 64 additions & 26 deletions src/base/charstr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ impl CharStr<[u8]> {
/// Checks whether an octets slice contains a correct character string.
fn check_slice(slice: &[u8]) -> Result<(), CharStrError> {
if slice.len() > CharStr::MAX_LEN {
Err(CharStrError)
Err(CharStrError(()))
} else {
Ok(())
}
Expand Down Expand Up @@ -316,7 +316,7 @@ impl CharStr<[u8]> {
while let Some(symbol) = Symbol::from_chars(&mut chars)? {
// We have the max length but there’s another character. Error!
if len == u8::MAX {
return Err(FromStrError::LongString);
return Err(PresentationErrorEnum::LongString.into());
}
target
.append_slice(&[symbol.into_octet()?])
Expand Down Expand Up @@ -745,7 +745,7 @@ impl<Builder: OctetsBuilder + AsRef<[u8]>> CharStrBuilder<Builder> {
/// returned.
pub fn from_builder(builder: Builder) -> Result<Self, CharStrError> {
if builder.as_ref().len() > CharStr::MAX_LEN {
Err(CharStrError)
Err(CharStrError(()))
} else {
Ok(unsafe { Self::from_builder_unchecked(builder) })
}
Expand Down Expand Up @@ -1093,8 +1093,8 @@ where
/// A byte sequence does not represent a valid character string.
///
/// This can only mean that the sequence is longer than 255 bytes.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CharStrError;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CharStrError(());

impl fmt::Display for CharStrError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Expand All @@ -1105,12 +1105,54 @@ impl fmt::Display for CharStrError {
#[cfg(feature = "std")]
impl std::error::Error for CharStrError {}

//------------ FromStrError --------------------------------------------
//------------ FromStrError --------------------------------------------------

/// An error happened when converting a Rust string to a DNS character string.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum FromStrError {
/// The string content was wrongly formatted.
Presentation(PresentationError),

/// The octet builder’s buffer was too short for the data.
ShortBuf,
}

//--- From

impl<T: Into<PresentationError>> From<T> for FromStrError {
fn from(err: T) -> Self {
Self::Presentation(err.into())
}
}

impl From<ShortBuf> for FromStrError {
fn from(_: ShortBuf) -> FromStrError {
FromStrError::ShortBuf
}
}

//--- Display and Error

impl fmt::Display for FromStrError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FromStrError::Presentation(ref err) => err.fmt(f),
FromStrError::ShortBuf => ShortBuf.fmt(f),
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for FromStrError {}

//------------ PresentationError ---------------------------------------------

/// An illegal presentation format was encountered.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PresentationError(PresentationErrorEnum);

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum PresentationErrorEnum {
/// A character string has more than 255 octets.
LongString,

Expand All @@ -1120,48 +1162,44 @@ pub enum FromStrError {
///
/// Only printable ASCII characters are allowed.
BadSymbol(BadSymbol),

/// The octet builder’s buffer was too short for the data.
ShortBuf,
}

//--- From

impl From<SymbolCharsError> for FromStrError {
fn from(err: SymbolCharsError) -> FromStrError {
FromStrError::SymbolChars(err)
impl From<SymbolCharsError> for PresentationError {
fn from(err: SymbolCharsError) -> Self {
Self(PresentationErrorEnum::SymbolChars(err))
}
}

impl From<BadSymbol> for FromStrError {
fn from(err: BadSymbol) -> FromStrError {
FromStrError::BadSymbol(err)
impl From<BadSymbol> for PresentationError {
fn from(err: BadSymbol) -> Self {
Self(PresentationErrorEnum::BadSymbol(err))
}
}

impl From<ShortBuf> for FromStrError {
fn from(_: ShortBuf) -> FromStrError {
FromStrError::ShortBuf
impl From<PresentationErrorEnum> for PresentationError {
fn from(err: PresentationErrorEnum) -> Self {
Self(err)
}
}

//--- Display and Error

impl fmt::Display for FromStrError {
impl fmt::Display for PresentationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FromStrError::LongString => {
match self.0 {
PresentationErrorEnum::LongString => {
f.write_str("character string with more than 255 octets")
}
FromStrError::SymbolChars(ref err) => err.fmt(f),
FromStrError::BadSymbol(ref err) => err.fmt(f),
FromStrError::ShortBuf => ShortBuf.fmt(f),
PresentationErrorEnum::SymbolChars(ref err) => err.fmt(f),
PresentationErrorEnum::BadSymbol(ref err) => err.fmt(f),
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for FromStrError {}
impl std::error::Error for PresentationError {}

//============ Testing =======================================================

Expand Down
14 changes: 7 additions & 7 deletions src/base/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ impl FromStr for Flags {
"AD" | "Ad" | "aD" | "ad" => flags.ad = true,
"CD" | "Cd" | "cD" | "cd" => flags.cd = true,
"" => {}
_ => return Err(FlagsFromStrError),
_ => return Err(FlagsFromStrError(())),
}
}
Ok(flags)
Expand Down Expand Up @@ -572,7 +572,7 @@ impl HeaderCounts {
self.set_qdcount(count);
Ok(())
}
None => Err(CountOverflow),
None => Err(CountOverflow(())),
}
}

Expand Down Expand Up @@ -611,7 +611,7 @@ impl HeaderCounts {
self.set_ancount(count);
Ok(())
}
None => Err(CountOverflow),
None => Err(CountOverflow(())),
}
}

Expand Down Expand Up @@ -650,7 +650,7 @@ impl HeaderCounts {
self.set_nscount(count);
Ok(())
}
None => Err(CountOverflow),
None => Err(CountOverflow(())),
}
}

Expand Down Expand Up @@ -689,7 +689,7 @@ impl HeaderCounts {
self.set_arcount(count);
Ok(())
}
None => Err(CountOverflow),
None => Err(CountOverflow(())),
}
}

Expand Down Expand Up @@ -909,7 +909,7 @@ impl AsMut<HeaderCounts> for HeaderSection {

/// An error happened when converting string to flags.
#[derive(Debug)]
pub struct FlagsFromStrError;
pub struct FlagsFromStrError(());

impl fmt::Display for FlagsFromStrError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -924,7 +924,7 @@ impl std::error::Error for FlagsFromStrError {}

/// An error happened while increasing a header count.
#[derive(Debug)]
pub struct CountOverflow;
pub struct CountOverflow(());

impl fmt::Display for CountOverflow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down
10 changes: 5 additions & 5 deletions src/base/iana/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ macro_rules! int_enum_str_with_decimal {
if let Ok(res) = s.parse() {
Ok($ianatype::from_int(res))
} else {
Err(FromStrError)
Err(FromStrError(()))
}
}
}
Expand Down Expand Up @@ -420,14 +420,14 @@ macro_rules! int_enum_str_with_prefix {
if l.eq_ignore_ascii_case($str_prefix) {
let value = match r.parse() {
Ok(x) => x,
Err(..) => return Err(FromStrError),
Err(..) => return Err(FromStrError(())),
};
Ok($ianatype::from_int(value))
} else {
Err(FromStrError)
Err(FromStrError(()))
}
} else {
Err(FromStrError)
Err(FromStrError(()))
}
}
}
Expand Down Expand Up @@ -505,7 +505,7 @@ macro_rules! scan_impl {
macro_rules! from_str_error {
($description:expr) => {
#[derive(Clone, Debug)]
pub struct FromStrError;
pub struct FromStrError(());

#[cfg(feature = "std")]
impl std::error::Error for FromStrError {
Expand Down
Loading

0 comments on commit 1466e1e

Please sign in to comment.