diff --git a/src/binary.rs b/src/binary.rs index bd00a8db..58416ee1 100644 --- a/src/binary.rs +++ b/src/binary.rs @@ -1,13 +1,18 @@ #! Module containing functionality related to BSON binary values. - mod vector; -use crate::{base64, spec::BinarySubtype, RawBinaryRef}; use std::{ - error, + convert::TryFrom, fmt::{self, Display}, }; +use crate::{ + base64, + error::{Error, Result}, + spec::BinarySubtype, + RawBinaryRef, +}; + pub use vector::{PackedBitVector, Vector}; /// Represents a BSON binary value. @@ -37,7 +42,7 @@ impl Binary { /// [`BinarySubtype::Generic`]. /// /// ```rust - /// # use bson::{Binary, binary::Result}; + /// # use bson::{Binary, error::Result}; /// # fn example() -> Result<()> { /// let input = base64::encode("hello"); /// let binary = Binary::from_base64(input, None)?; @@ -50,9 +55,7 @@ impl Binary { input: impl AsRef, subtype: impl Into>, ) -> Result { - let bytes = base64::decode(input.as_ref()).map_err(|e| Error::DecodingError { - message: e.to_string(), - })?; + let bytes = base64::decode(input.as_ref()).map_err(Error::binary)?; let subtype = match subtype.into() { Some(s) => s, None => BinarySubtype::Generic, @@ -97,27 +100,3 @@ impl Binary { } } } - -/// Possible errors that can arise during [`Binary`] construction. -#[derive(Clone, Debug)] -#[non_exhaustive] -pub enum Error { - /// While trying to decode from base64, an error was returned. - DecodingError { message: String }, - - /// A [`Vector`]-related error occurred. - Vector { message: String }, -} - -impl error::Error for Error {} - -impl std::fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::DecodingError { message } => fmt.write_str(message), - Error::Vector { message } => fmt.write_str(message), - } - } -} - -pub type Result = std::result::Result; diff --git a/src/binary/vector.rs b/src/binary/vector.rs index d67f3757..a427dfea 100644 --- a/src/binary/vector.rs +++ b/src/binary/vector.rs @@ -23,7 +23,7 @@ const PACKED_BIT: u8 = 0x10; /// /// ```rust /// # use serde::{Serialize, Deserialize}; -/// # use bson::{binary::{Result, Vector}, spec::ElementType}; +/// # use bson::{binary::Vector, error::Result, spec::ElementType}; /// #[derive(Serialize, Deserialize)] /// struct Data { /// vector: Vector, @@ -64,7 +64,7 @@ impl PackedBitVector { /// single-bit elements in little-endian format. For example, the following vector: /// /// ```rust - /// # use bson::binary::{Result, PackedBitVector}; + /// # use bson::{binary::PackedBitVector, error::Result}; /// # fn main() -> Result<()> { /// let packed_bits = vec![238, 224]; /// let vector = PackedBitVector::new(packed_bits, 0)?; @@ -91,17 +91,14 @@ impl PackedBitVector { pub fn new(vector: Vec, padding: impl Into>) -> Result { let padding = padding.into().unwrap_or(0); if !(0..8).contains(&padding) { - return Err(Error::Vector { - message: format!("padding must be within 0-7 inclusive, got {}", padding), - }); + return Err(Error::binary(format!( + "vector padding must be within 0-7 inclusive, got {padding}" + ))); } if padding != 0 && vector.is_empty() { - return Err(Error::Vector { - message: format!( - "cannot specify non-zero padding if the provided vector is empty, got {}", - padding - ), - }); + return Err(Error::binary(format!( + "cannot specify non-zero padding if the provided vector is empty, got {padding}", + ))); } Ok(Self { vector, padding }) } @@ -115,24 +112,19 @@ impl Vector { let bytes = bytes.as_ref(); if bytes.len() < 2 { - return Err(Error::Vector { - message: format!( - "the provided bytes must have a length of at least 2, got {}", - bytes.len() - ), - }); + return Err(Error::binary(format!( + "the provided vector bytes must have a length of at least 2, got {}", + bytes.len() + ))); } let d_type = bytes[0]; let padding = bytes[1]; if d_type != PACKED_BIT && padding != 0 { - return Err(Error::Vector { - message: format!( - "padding can only be specified for a packed bit vector (data type {}), got \ - type {}", - PACKED_BIT, d_type - ), - }); + return Err(Error::binary(format!( + "padding can only be specified for a packed bit vector (data type {}), got type {}", + PACKED_BIT, d_type + ))); } let number_bytes = &bytes[2..]; @@ -149,11 +141,11 @@ impl Vector { let mut vector = Vec::new(); for chunk in number_bytes.chunks(F32_BYTES) { - let bytes: [u8; F32_BYTES] = chunk.try_into().map_err(|_| Error::Vector { - message: format!( + let bytes: [u8; F32_BYTES] = chunk.try_into().map_err(|_| { + Error::binary(format!( "f32 vector values must be {} bytes, got {:?}", F32_BYTES, chunk, - ), + )) })?; vector.push(f32::from_le_bytes(bytes)); } @@ -163,9 +155,9 @@ impl Vector { let packed_bit_vector = PackedBitVector::new(number_bytes.to_vec(), padding)?; Ok(Self::PackedBit(packed_bit_vector)) } - other => Err(Error::Vector { - message: format!("unsupported vector data type: {}", other), - }), + other => Err(Error::binary(format!( + "unsupported vector data type: {other}" + ))), } } @@ -228,9 +220,10 @@ impl TryFrom<&Binary> for Vector { fn try_from(binary: &Binary) -> Result { if binary.subtype != BinarySubtype::Vector { - return Err(Error::Vector { - message: format!("expected vector binary subtype, got {:?}", binary.subtype), - }); + return Err(Error::binary(format!( + "expected vector binary subtype, got {:?}", + binary.subtype + ))); } Self::from_bytes(&binary.bytes) } diff --git a/src/datetime.rs b/src/datetime.rs index 41dca1df..e87e342b 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,18 +1,13 @@ //! Module containing functionality related to BSON DateTimes. //! For more information, see the documentation for the [`DateTime`] type. +pub(crate) mod builder; use std::{ convert::TryInto, - error, fmt::{self, Display}, - result, time::{Duration, SystemTime}, }; -pub(crate) mod builder; -pub use crate::datetime::builder::DateTimeBuilder; -use time::format_description::well_known::Rfc3339; - #[cfg(feature = "chrono-0_4")] use chrono::{LocalResult, TimeZone, Utc}; #[cfg(all( @@ -20,6 +15,10 @@ use chrono::{LocalResult, TimeZone, Utc}; any(feature = "chrono-0_4", feature = "time-0_3") ))] use serde::{Deserialize, Deserializer, Serialize}; +use time::format_description::well_known::Rfc3339; + +pub use crate::datetime::builder::DateTimeBuilder; +use crate::error::{Error, Result}; /// Struct representing a BSON datetime. /// Note: BSON datetimes have millisecond precision. @@ -388,21 +387,13 @@ impl crate::DateTime { /// Convert this [`DateTime`] to an RFC 3339 formatted string. pub fn try_to_rfc3339_string(self) -> Result { - self.to_time_0_3() - .format(&Rfc3339) - .map_err(|e| Error::CannotFormat { - message: e.to_string(), - }) + self.to_time_0_3().format(&Rfc3339).map_err(Error::datetime) } /// Convert the given RFC 3339 formatted string to a [`DateTime`], truncating it to millisecond /// precision. pub fn parse_rfc3339_str(s: impl AsRef) -> Result { - let odt = time::OffsetDateTime::parse(s.as_ref(), &Rfc3339).map_err(|e| { - Error::InvalidTimestamp { - message: e.to_string(), - } - })?; + let odt = time::OffsetDateTime::parse(s.as_ref(), &Rfc3339).map_err(Error::datetime)?; Ok(Self::from_time_0_3(odt)) } @@ -543,30 +534,3 @@ impl serde_with::SerializeAs for crate::DateTime { dt.serialize(serializer) } } - -/// Errors that can occur during [`DateTime`] construction and generation. -#[derive(Clone, Debug)] -#[non_exhaustive] -pub enum Error { - /// Error returned when an invalid datetime format is provided to a conversion method. - #[non_exhaustive] - InvalidTimestamp { message: String }, - /// Error returned when a [`DateTime`] cannot be represented in a particular format. - #[non_exhaustive] - CannotFormat { message: String }, -} - -/// Alias for `Result` -pub type Result = result::Result; - -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::InvalidTimestamp { message } | Error::CannotFormat { message } => { - write!(fmt, "{}", message) - } - } - } -} - -impl error::Error for Error {} diff --git a/src/datetime/builder.rs b/src/datetime/builder.rs index c16e22e2..d8cd8ff8 100644 --- a/src/datetime/builder.rs +++ b/src/datetime/builder.rs @@ -1,7 +1,12 @@ -use super::*; use std::convert::TryFrom; + use time::Date; +use crate::{ + datetime::DateTime, + error::{Error, Result}, +}; + /// Builder for constructing a BSON [`DateTime`] pub struct DateTimeBuilder { pub(crate) year: Y, @@ -169,19 +174,16 @@ impl DateTimeBuilder { /// /// Note: You cannot call `build()` before setting at least the year, month and day. pub fn build(self) -> Result { - let err = |e: time::error::ComponentRange| Error::InvalidTimestamp { - message: e.to_string(), - }; - let month = time::Month::try_from(self.month.0).map_err(err)?; + let month = time::Month::try_from(self.month.0).map_err(Error::datetime)?; let dt = Date::from_calendar_date(self.year.0, month, self.day.0) - .map_err(err)? + .map_err(Error::datetime)? .with_hms_milli( self.hour.unwrap_or(0), self.minute.unwrap_or(0), self.second.unwrap_or(0), self.millisecond.unwrap_or(0), ) - .map_err(err)?; + .map_err(Error::datetime)?; Ok(DateTime::from_time_private(dt.assume_utc())) } } diff --git a/src/decimal128.rs b/src/decimal128.rs index 036b1f77..b827e986 100644 --- a/src/decimal128.rs +++ b/src/decimal128.rs @@ -1,9 +1,11 @@ //! [BSON Decimal128](https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst) data type representation -use std::{convert::TryInto, fmt}; +use std::{convert::TryInto, fmt, num::ParseIntError}; use bitvec::prelude::*; +use crate::error::{Decimal128ErrorKind, Error, Result}; + /// Struct representing a BSON Decimal128 type. /// /// This type supports conversion to and from human-readable strings via the [std::fmt::Display] and @@ -60,9 +62,9 @@ impl fmt::Display for Decimal128 { } impl std::str::FromStr for Decimal128 { - type Err = ParseError; + type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { Ok(s.parse::()?.pack()) } } @@ -138,10 +140,7 @@ impl Coefficient { /// The maximum allowable value of a coefficient. const MAX_VALUE: u128 = 9_999_999_999_999_999_999_999_999_999_999_999; - fn from_bits( - src_prefix: &BitSlice, - src_suffix: &BitSlice, - ) -> Result { + fn from_bits(src_prefix: &BitSlice, src_suffix: &BitSlice) -> Result { let mut bytes = [0u8; 16]; let bits = &mut bytes.view_bits_mut::()[Self::UNUSED_BITS..]; let prefix_len = src_prefix.len(); @@ -149,7 +148,7 @@ impl Coefficient { bits[prefix_len..].copy_from_bitslice(src_suffix); let out = Self(bytes); if out.value() > Self::MAX_VALUE { - Err(ParseError::Overflow) + Err(Error::decimal128(Decimal128ErrorKind::Overflow {})) } else { Ok(out) } @@ -325,46 +324,10 @@ impl fmt::Display for ParsedDecimal128 { } } -#[derive(Debug)] -#[non_exhaustive] -pub enum ParseError { - EmptyExponent, - InvalidExponent(std::num::ParseIntError), - InvalidCoefficient(std::num::ParseIntError), - Overflow, - Underflow, - InexactRounding, - Unparseable, -} - -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ParseError::EmptyExponent => write!(f, "empty exponent"), - ParseError::InvalidExponent(e) => write!(f, "invalid exponent: {}", e), - ParseError::InvalidCoefficient(e) => write!(f, "invalid coefficient: {}", e), - ParseError::Overflow => write!(f, "overflow"), - ParseError::Underflow => write!(f, "underflow"), - ParseError::InexactRounding => write!(f, "inexact rounding"), - ParseError::Unparseable => write!(f, "unparseable"), - } - } -} - -impl std::error::Error for ParseError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - ParseError::InvalidExponent(e) => Some(e), - ParseError::InvalidCoefficient(e) => Some(e), - _ => None, - } - } -} - impl std::str::FromStr for ParsedDecimal128 { - type Err = ParseError; + type Err = Error; - fn from_str(mut s: &str) -> Result { + fn from_str(mut s: &str) -> Result { let sign; if let Some(rest) = s.strip_prefix(&['-', '+'][..]) { sign = s.starts_with('-'); @@ -385,21 +348,28 @@ impl std::str::FromStr for ParsedDecimal128 { decimal_str = finite_str; exp_str = "0"; } - Some((_, "")) => return Err(ParseError::EmptyExponent), + Some((_, "")) => { + return Err(Error::decimal128(Decimal128ErrorKind::EmptyExponent {})) + } Some((pre, post)) => { decimal_str = pre; exp_str = post; } } - let mut exp = exp_str - .parse::() - .map_err(ParseError::InvalidExponent)?; + let mut exp = exp_str.parse::().map_err(|e| { + Error::decimal128(Decimal128ErrorKind::InvalidExponent {}).with_message(e) + })?; // Remove decimal point and adjust exponent let joined_str; if let Some((pre, post)) = decimal_str.split_once('.') { - let exp_adj = post.len().try_into().map_err(|_| ParseError::Underflow)?; - exp = exp.checked_sub(exp_adj).ok_or(ParseError::Underflow)?; + let exp_adj = post + .len() + .try_into() + .map_err(|_| Error::decimal128(Decimal128ErrorKind::Underflow {}))?; + exp = exp + .checked_sub(exp_adj) + .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Underflow {}))?; joined_str = format!("{}{}", pre, post); decimal_str = &joined_str; } @@ -415,8 +385,10 @@ impl std::str::FromStr for ParsedDecimal128 { decimal_str = round_decimal_str(decimal_str, Coefficient::MAX_DIGITS)?; let exp_adj = (len - decimal_str.len()) .try_into() - .map_err(|_| ParseError::Overflow)?; - exp = exp.checked_add(exp_adj).ok_or(ParseError::Overflow)?; + .map_err(|_| Error::decimal128(Decimal128ErrorKind::Overflow {}))?; + exp = exp + .checked_add(exp_adj) + .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Overflow {}))?; } } @@ -425,11 +397,11 @@ impl std::str::FromStr for ParsedDecimal128 { if decimal_str != "0" { let delta = (Exponent::TINY - exp) .try_into() - .map_err(|_| ParseError::Overflow)?; + .map_err(|_| Error::decimal128(Decimal128ErrorKind::Overflow {}))?; let new_precision = decimal_str .len() .checked_sub(delta) - .ok_or(ParseError::Underflow)?; + .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Overflow {}))?; decimal_str = round_decimal_str(decimal_str, new_precision)?; } exp = Exponent::TINY; @@ -439,14 +411,14 @@ impl std::str::FromStr for ParsedDecimal128 { if decimal_str != "0" { let delta = (exp - Exponent::MAX) .try_into() - .map_err(|_| ParseError::Overflow)?; + .map_err(|_| Error::decimal128(Decimal128ErrorKind::Overflow {}))?; if decimal_str .len() .checked_add(delta) - .ok_or(ParseError::Overflow)? + .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Overflow {}))? > Coefficient::MAX_DIGITS { - return Err(ParseError::Overflow); + return Err(Error::decimal128(Decimal128ErrorKind::Overflow {})); } padded_str = format!("{}{}", decimal_str, "0".repeat(delta)); decimal_str = &padded_str; @@ -456,9 +428,9 @@ impl std::str::FromStr for ParsedDecimal128 { // Assemble the final value let exponent = Exponent::from_native(exp); - let coeff: u128 = decimal_str - .parse() - .map_err(ParseError::InvalidCoefficient)?; + let coeff: u128 = decimal_str.parse().map_err(|e: ParseIntError| { + Error::decimal128(Decimal128ErrorKind::InvalidCoefficient {}).with_message(e) + })?; let coefficient = Coefficient::from_native(coeff); Decimal128Kind::Finite { exponent, @@ -471,17 +443,13 @@ impl std::str::FromStr for ParsedDecimal128 { } } -fn round_decimal_str(s: &str, precision: usize) -> Result<&str, ParseError> { - // TODO: In 1.80+ there's split_at_checked to make sure the split doesn't - // panic if the index doesn't falls at a codepoint boundary, until then - // we can check it with s.is_char_boundary(precision) - if !s.is_char_boundary(precision) { - return Err(ParseError::Unparseable); - } - let (pre, post) = s.split_at(precision); +fn round_decimal_str(s: &str, precision: usize) -> Result<&str> { + let (pre, post) = s + .split_at_checked(precision) + .ok_or_else(|| Error::decimal128(Decimal128ErrorKind::Unparseable {}))?; // Any nonzero trimmed digits mean it would be an imprecise round. if post.chars().any(|c| c != '0') { - return Err(ParseError::InexactRounding); + return Err(Error::decimal128(Decimal128ErrorKind::InexactRounding {})); } Ok(pre) } diff --git a/src/error.rs b/src/error.rs index 20380dcf..d3ed5740 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,16 +1,27 @@ +mod decimal128; +mod oid; +mod uuid; +mod value_access; + use thiserror::Error; -use crate::spec::ElementType; +pub use decimal128::Decimal128ErrorKind; +pub use oid::ObjectIdErrorKind; +pub use uuid::UuidErrorKind; +pub use value_access::ValueAccessErrorKind; pub type Result = std::result::Result; /// An error that can occur in the `bson` crate. -#[derive(Debug, Error)] +#[derive(Clone, Debug, Error)] #[non_exhaustive] pub struct Error { /// The kind of error that occurred. pub kind: ErrorKind, + /// An optional message describing the error. + pub message: Option, + /// The document key associated with the error, if any. pub key: Option, @@ -20,28 +31,69 @@ pub struct Error { impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "BSON error")?; + if let Some(key) = self.key.as_deref() { - write!(f, "Error at key \"{key}\": ")?; + write!(f, " at key \"{key}\"")?; } else if let Some(index) = self.index { - write!(f, "Error at array index {index}: ")?; + write!(f, " at array index {index}")?; } - write!(f, "{}", self.kind) + write!(f, ". Kind: {}", self.kind)?; + if let Some(ref message) = self.message { + write!(f, ". Message: {}", message)?; + } + write!(f, ".") } } /// The types of errors that can occur in the `bson` crate. -#[derive(Debug, Error)] +#[derive(Clone, Debug, Error)] #[non_exhaustive] pub enum ErrorKind { + /// An error related to the [`Binary`](crate::Binary) type occurred. + #[error("A Binary-related error occurred")] + #[non_exhaustive] + Binary {}, + + /// An error related to the [`DateTime`](crate::DateTime) type occurred. + #[error("A DateTime-related error occurred")] + #[non_exhaustive] + DateTime {}, + + /// An error related to the [`Decimal128`](crate::Decimal128) type occurred. + #[error("A Decimal128-related error occurred: {kind}")] + #[non_exhaustive] + Decimal128 { + /// The kind of error that occurred. + kind: Decimal128ErrorKind, + }, + /// Malformed BSON bytes were encountered. - #[error("Malformed BSON: {message}")] + #[error("Malformed BSON bytes")] #[non_exhaustive] - MalformedValue { message: String }, + MalformedBytes {}, + + /// An error related to the [`ObjectId`](crate::oid::ObjectId) type occurred. + #[error("An ObjectId-related error occurred: {kind}")] + #[non_exhaustive] + ObjectId { + /// The kind of error that occurred. + kind: ObjectIdErrorKind, + }, /// Invalid UTF-8 bytes were encountered. #[error("Invalid UTF-8")] - Utf8Encoding, + #[non_exhaustive] + Utf8Encoding {}, + + /// An error related to the [`Uuid`](crate::uuid::Uuid) type occurred. + #[error("A UUID-related error occurred: {kind}")] + #[non_exhaustive] + Uuid { + /// The kind of error that occurred. + kind: UuidErrorKind, + }, /// An error occurred when attempting to access a value in a document. #[error("An error occurred when attempting to access a document value: {kind}")] @@ -52,8 +104,9 @@ pub enum ErrorKind { }, /// A [`std::io::Error`] occurred. - #[error("An IO error occurred: {0}")] - Io(std::io::Error), + #[error("An IO error occurred")] + #[non_exhaustive] + Io {}, /// A wrapped deserialization error. /// TODO RUST-1406: collapse this @@ -68,13 +121,14 @@ impl From for Error { kind, key: None, index: None, + message: None, } } } impl From for Error { fn from(value: std::io::Error) -> Self { - ErrorKind::Io(value).into() + Error::from(ErrorKind::Io {}).with_message(value) } } @@ -85,31 +139,6 @@ impl From for Error { } } -/// The types of errors that can occur when attempting to access a value in a document. -#[derive(Debug, Error)] -#[non_exhaustive] -pub enum ValueAccessErrorKind { - /// No value for the specified key was present in the document. - #[error("The key was not present in the document")] - NotPresent, - - /// The type of the value in the document did not match the requested type. - #[error("Expected type {expected:?}, got type {actual:?}")] - #[non_exhaustive] - UnexpectedType { - /// The actual type of the value. - actual: ElementType, - - /// The expected type of the value. - expected: ElementType, - }, - - /// An error occurred when attempting to parse the document's BSON bytes. - #[error("{message}")] - #[non_exhaustive] - InvalidBson { message: String }, -} - impl Error { pub(crate) fn with_key(mut self, key: impl Into) -> Self { self.key = Some(key.into()); @@ -121,58 +150,25 @@ impl Error { self } - pub(crate) fn value_access_not_present() -> Self { - ErrorKind::ValueAccess { - kind: ValueAccessErrorKind::NotPresent, - } - .into() - } - - pub(crate) fn value_access_unexpected_type(actual: ElementType, expected: ElementType) -> Self { - ErrorKind::ValueAccess { - kind: ValueAccessErrorKind::UnexpectedType { actual, expected }, - } - .into() - } - - pub(crate) fn value_access_invalid_bson(message: String) -> Self { - ErrorKind::ValueAccess { - kind: ValueAccessErrorKind::InvalidBson { message }, - } - .into() + pub(crate) fn with_message(mut self, message: impl ToString) -> Self { + self.message = Some(message.to_string()); + self } - pub(crate) fn malformed_value(message: impl ToString) -> Self { - ErrorKind::MalformedValue { - message: message.to_string(), - } - .into() + pub(crate) fn binary(message: impl ToString) -> Self { + Self::from(ErrorKind::Binary {}).with_message(message) } - #[cfg(test)] - pub(crate) fn is_value_access_not_present(&self) -> bool { - matches!( - self.kind, - ErrorKind::ValueAccess { - kind: ValueAccessErrorKind::NotPresent, - .. - } - ) + pub(crate) fn datetime(message: impl ToString) -> Self { + Self::from(ErrorKind::DateTime {}).with_message(message) } - #[cfg(test)] - pub(crate) fn is_value_access_unexpected_type(&self) -> bool { - matches!( - self.kind, - ErrorKind::ValueAccess { - kind: ValueAccessErrorKind::UnexpectedType { .. }, - .. - } - ) + pub(crate) fn malformed_bytes(message: impl ToString) -> Self { + Self::from(ErrorKind::MalformedBytes {}).with_message(message) } #[cfg(all(test, feature = "serde"))] - pub(crate) fn is_malformed_value(&self) -> bool { - matches!(self.kind, ErrorKind::MalformedValue { .. },) + pub(crate) fn is_malformed_bytes(&self) -> bool { + matches!(self.kind, ErrorKind::MalformedBytes { .. },) } } diff --git a/src/error/decimal128.rs b/src/error/decimal128.rs new file mode 100644 index 00000000..ab40eed1 --- /dev/null +++ b/src/error/decimal128.rs @@ -0,0 +1,59 @@ +use thiserror::Error as ThisError; + +use crate::error::{Error, ErrorKind}; + +/// The kinds of errors that can occur when working with the [`Decimal128`](crate::Decimal128) type. +#[derive(Clone, Debug, ThisError)] +#[non_exhaustive] +pub enum Decimal128ErrorKind { + /// Empty exponent. + #[error("empty exponent")] + #[non_exhaustive] + EmptyExponent {}, + + /// Invalid exponent. + #[error("invalid exponent")] + #[non_exhaustive] + InvalidExponent {}, + + /// Invalid coefficient. + #[error("invalid coefficient")] + #[non_exhaustive] + InvalidCoefficient {}, + + /// Overflow. + #[error("overflow")] + #[non_exhaustive] + Overflow {}, + + /// Underflow. + #[error("underflow")] + #[non_exhaustive] + Underflow {}, + + /// Inexact rounding. + #[error("inexact rounding")] + #[non_exhaustive] + InexactRounding {}, + + /// Unparseable. + #[error("unparseable")] + #[non_exhaustive] + Unparseable {}, +} + +impl Error { + pub(crate) fn decimal128(kind: Decimal128ErrorKind) -> Self { + ErrorKind::Decimal128 { kind }.into() + } + + #[cfg(test)] + pub(crate) fn is_decimal128_unparseable(&self) -> bool { + matches!( + self.kind, + ErrorKind::Decimal128 { + kind: Decimal128ErrorKind::Unparseable {}, + } + ) + } +} diff --git a/src/error/oid.rs b/src/error/oid.rs new file mode 100644 index 00000000..6c770d88 --- /dev/null +++ b/src/error/oid.rs @@ -0,0 +1,52 @@ +use hex::FromHexError; +use thiserror::Error as ThisError; + +use crate::error::{Error, ErrorKind}; + +/// The kinds of errors that can occur when working with the [`ObjectId`](crate::oid::ObjectId) +/// type. +#[derive(Clone, Debug, ThisError)] +#[non_exhaustive] +pub enum ObjectIdErrorKind { + /// An invalid character was found in the provided hex string. Valid characters are: `0...9`, + /// `a...f`, or `A...F`. + #[error("invalid character '{c}' encountered at index {index}")] + #[non_exhaustive] + InvalidHexStringCharacter { + /// The invalid character. + c: char, + + /// The index at which the invalid character was encountered. + index: usize, + }, + + /// An `ObjectId` with an invalid length was encountered. + #[error("invalid hex string length {length}")] + #[non_exhaustive] + InvalidHexStringLength { + /// The length of the invalid hex string. + length: usize, + }, +} + +impl Error { + // This method is not a From implementation so that it is not part of the public API. + pub(crate) fn from_hex_error(error: FromHexError, length: usize) -> Self { + let kind = match error { + FromHexError::InvalidHexCharacter { c, index } => { + ObjectIdErrorKind::InvalidHexStringCharacter { c, index } + } + FromHexError::InvalidStringLength | FromHexError::OddLength => { + ObjectIdErrorKind::InvalidHexStringLength { length } + } + }; + ErrorKind::ObjectId { kind }.into() + } + + pub(crate) fn oid_invalid_length(length: usize) -> Self { + ErrorKind::ObjectId { + kind: ObjectIdErrorKind::InvalidHexStringLength { length }, + } + .into() + } +} diff --git a/src/error/uuid.rs b/src/error/uuid.rs new file mode 100644 index 00000000..323dbd18 --- /dev/null +++ b/src/error/uuid.rs @@ -0,0 +1,74 @@ +use thiserror::Error as ThisError; + +use crate::{ + error::{Error, ErrorKind}, + spec::BinarySubtype, + UuidRepresentation, +}; + +/// The kinds of errors that can occur when working with the [`Uuid`](crate::uuid::Uuid) type. +#[derive(Clone, Debug, ThisError)] +#[non_exhaustive] +pub enum UuidErrorKind { + /// An invalid string was used to construct a UUID. + #[error("invalid UUID string")] + #[non_exhaustive] + InvalidString {}, + + /// The requested `UuidRepresentation` does not match the binary subtype of a `Binary` + /// value. + #[error( + "expected binary subtype {expected_binary_subtype:?} for representation \ + {requested_representation:?}, got {actual_binary_subtype:?}" + )] + #[non_exhaustive] + RepresentationMismatch { + /// The subtype that was expected given the requested representation. + expected_binary_subtype: BinarySubtype, + + /// The actual subtype of the binary value. + actual_binary_subtype: BinarySubtype, + + /// The requested representation. + requested_representation: UuidRepresentation, + }, + + /// An invalid length of bytes was used to construct a UUID value. + #[error("expected length of 16 bytes, got {length}")] + #[non_exhaustive] + InvalidLength { + /// The actual length of the data. + length: usize, + }, +} + +impl Error { + pub(crate) fn invalid_uuid_string(message: impl ToString) -> Self { + Self::from(ErrorKind::Uuid { + kind: UuidErrorKind::InvalidString {}, + }) + .with_message(message) + } + + pub(crate) fn uuid_representation_mismatch( + requested_representation: UuidRepresentation, + actual_binary_subtype: BinarySubtype, + expected_binary_subtype: BinarySubtype, + ) -> Self { + ErrorKind::Uuid { + kind: UuidErrorKind::RepresentationMismatch { + expected_binary_subtype, + actual_binary_subtype, + requested_representation, + }, + } + .into() + } + + pub(crate) fn invalid_uuid_length(length: usize) -> Self { + ErrorKind::Uuid { + kind: UuidErrorKind::InvalidLength { length }, + } + .into() + } +} diff --git a/src/error/value_access.rs b/src/error/value_access.rs new file mode 100644 index 00000000..ffc82f4d --- /dev/null +++ b/src/error/value_access.rs @@ -0,0 +1,77 @@ +use thiserror::Error as ThisError; + +use crate::{ + error::{Error, ErrorKind}, + spec::ElementType, +}; + +/// The types of errors that can occur when attempting to access a value in a document. +#[derive(Clone, Debug, ThisError)] +#[non_exhaustive] +pub enum ValueAccessErrorKind { + /// No value for the specified key was present in the document. + #[error("the key was not present in the document")] + #[non_exhaustive] + NotPresent {}, + + /// The type of the value in the document did not match the requested type. + #[error("expected type {expected:?}, got type {actual:?}")] + #[non_exhaustive] + UnexpectedType { + /// The actual type of the value. + actual: ElementType, + + /// The expected type of the value. + expected: ElementType, + }, + + /// An error occurred when attempting to parse the document's BSON bytes. + #[error("invalid BSON bytes")] + #[non_exhaustive] + InvalidBson {}, +} + +impl Error { + pub(crate) fn value_access_not_present() -> Self { + ErrorKind::ValueAccess { + kind: ValueAccessErrorKind::NotPresent {}, + } + .into() + } + + pub(crate) fn value_access_unexpected_type(actual: ElementType, expected: ElementType) -> Self { + ErrorKind::ValueAccess { + kind: ValueAccessErrorKind::UnexpectedType { actual, expected }, + } + .into() + } + + pub(crate) fn value_access_invalid_bson(message: String) -> Self { + Self::from(ErrorKind::ValueAccess { + kind: ValueAccessErrorKind::InvalidBson {}, + }) + .with_message(message) + } + + #[cfg(test)] + pub(crate) fn is_value_access_not_present(&self) -> bool { + matches!( + self.kind, + ErrorKind::ValueAccess { + kind: ValueAccessErrorKind::NotPresent {}, + .. + } + ) + } + + #[cfg(test)] + pub(crate) fn is_value_access_unexpected_type(&self) -> bool { + matches!( + self.kind, + ErrorKind::ValueAccess { + kind: ValueAccessErrorKind::UnexpectedType { .. }, + .. + } + ) + } +} diff --git a/src/extjson/de.rs b/src/extjson/de.rs index 82b8fcde..f147ad43 100644 --- a/src/extjson/de.rs +++ b/src/extjson/de.rs @@ -25,14 +25,14 @@ use std::convert::{TryFrom, TryInto}; use serde::de::{Error as _, Unexpected}; -use crate::{extjson::models, oid, Bson, Document}; +use crate::{extjson::models, Bson, Document}; #[derive(Clone, Debug)] #[non_exhaustive] /// Error cases that can occur during deserialization from [extended JSON](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/). pub enum Error { /// Errors that can occur during OID construction and generation from the input data. - InvalidObjectId(oid::Error), + InvalidObjectId(crate::error::Error), /// A general error encountered during deserialization. /// See: @@ -69,12 +69,6 @@ impl From for Error { } } -impl From for Error { - fn from(err: oid::Error) -> Self { - Self::InvalidObjectId(err) - } -} - pub type Result = std::result::Result; /// This converts from the input JSON object as if it were [MongoDB Extended JSON v2](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/). diff --git a/src/extjson/models.rs b/src/extjson/models.rs index 297b47d6..caccd12f 100644 --- a/src/extjson/models.rs +++ b/src/extjson/models.rs @@ -100,7 +100,8 @@ pub(crate) struct ObjectId { impl ObjectId { pub(crate) fn parse(self) -> extjson::de::Result { - let oid = oid::ObjectId::parse_str(self.oid.as_str())?; + let oid = oid::ObjectId::parse_str(self.oid.as_str()) + .map_err(extjson::de::Error::InvalidObjectId)?; Ok(oid) } } diff --git a/src/oid.rs b/src/oid.rs index 074b4a06..bb409f5e 100644 --- a/src/oid.rs +++ b/src/oid.rs @@ -1,21 +1,19 @@ //! Module containing functionality related to BSON ObjectIds. //! For more information, see the documentation for the [`ObjectId`] type. +#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] +use std::{convert::TryInto, time::SystemTime}; use std::{ - error, fmt, - result, str::FromStr, sync::atomic::{AtomicUsize, Ordering}, }; -#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] -use std::{convert::TryInto, time::SystemTime}; - -use hex::{self, FromHexError}; use once_cell::sync::Lazy; use rand::{random, rng, Rng}; +use crate::error::{Error, Result}; + const TIMESTAMP_SIZE: usize = 4; const PROCESS_ID_SIZE: usize = 5; const COUNTER_SIZE: usize = 3; @@ -29,49 +27,6 @@ const MAX_U24: usize = 0xFF_FFFF; static OID_COUNTER: Lazy = Lazy::new(|| AtomicUsize::new(rng().random_range(0..=MAX_U24))); -/// Errors that can occur during [`ObjectId`] construction and generation. -#[derive(Clone, Debug)] -#[non_exhaustive] -pub enum Error { - /// An invalid character was found in the provided hex string. Valid characters are: `0...9`, - /// `a...f`, or `A...F`. - #[non_exhaustive] - InvalidHexStringCharacter { c: char, index: usize, hex: String }, - - /// An [`ObjectId`]'s hex string representation must be an exactly 12-byte (24-char) - /// hexadecimal string. - #[non_exhaustive] - InvalidHexStringLength { length: usize, hex: String }, -} - -/// Alias for Result. -pub type Result = result::Result; - -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::InvalidHexStringCharacter { c, index, hex } => { - write!( - fmt, - "invalid character '{}' was found at index {} in the provided hex string: \ - \"{}\"", - c, index, hex - ) - } - Error::InvalidHexStringLength { length, hex } => { - write!( - fmt, - "provided hex string representation must be exactly 12 bytes, instead got: \ - \"{}\", length {}", - hex, length - ) - } - } - } -} - -impl error::Error for Error {} - /// A wrapper around a raw 12-byte ObjectId. /// /// ## `serde` integration @@ -198,24 +153,10 @@ impl ObjectId { pub fn parse_str(s: impl AsRef) -> Result { let s = s.as_ref(); - let bytes: Vec = hex::decode(s.as_bytes()).map_err(|e| match e { - FromHexError::InvalidHexCharacter { c, index } => Error::InvalidHexStringCharacter { - c, - index, - hex: s.to_string(), - }, - FromHexError::InvalidStringLength | FromHexError::OddLength => { - Error::InvalidHexStringLength { - length: s.len(), - hex: s.to_string(), - } - } - })?; + let bytes: Vec = + hex::decode(s.as_bytes()).map_err(|e| Error::from_hex_error(e, s.len()))?; if bytes.len() != 12 { - Err(Error::InvalidHexStringLength { - length: s.len(), - hex: s.to_string(), - }) + Err(Error::oid_invalid_length(bytes.len())) } else { let mut byte_array: [u8; 12] = [0; 12]; byte_array[..].copy_from_slice(&bytes[..]); diff --git a/src/raw.rs b/src/raw.rs index 9537e3da..b1a2c8be 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -173,7 +173,7 @@ fn f64_from_slice(val: &[u8]) -> Result { .get(0..8) .and_then(|s| s.try_into().ok()) .ok_or_else(|| { - Error::malformed_value(format!( + Error::malformed_bytes(format!( "expected 8 bytes to read double, instead got {}", val.len() )) @@ -188,7 +188,7 @@ fn i32_from_slice(val: &[u8]) -> Result { .get(0..4) .and_then(|s| s.try_into().ok()) .ok_or_else(|| { - Error::malformed_value(format!( + Error::malformed_bytes(format!( "expected 4 bytes to read i32, instead got {}", val.len() )) @@ -203,7 +203,7 @@ fn i64_from_slice(val: &[u8]) -> Result { .get(0..8) .and_then(|s| s.try_into().ok()) .ok_or_else(|| { - Error::malformed_value(format!( + Error::malformed_bytes(format!( "expected 8 bytes to read i64, instead got {}", val.len() )) @@ -216,7 +216,7 @@ fn u8_from_slice(val: &[u8]) -> Result { .get(0..1) .and_then(|s| s.try_into().ok()) .ok_or_else(|| { - Error::malformed_value(format!( + Error::malformed_bytes(format!( "expected 1 byte to read u8, instead got {}", val.len() )) @@ -227,7 +227,7 @@ fn u8_from_slice(val: &[u8]) -> Result { pub(crate) fn bool_from_slice(val: &[u8]) -> Result { let val = u8_from_slice(val)?; if val > 1 { - return Err(Error::malformed_value(format!( + return Err(Error::malformed_bytes(format!( "boolean must be stored as 0 or 1, got {}", val ))); @@ -238,7 +238,7 @@ pub(crate) fn bool_from_slice(val: &[u8]) -> Result { fn read_len(buf: &[u8]) -> Result { if buf.len() < 4 { - return Err(Error::malformed_value(format!( + return Err(Error::malformed_bytes(format!( "expected buffer with string to contain at least 4 bytes, but it only has {}", buf.len() ))); @@ -248,14 +248,14 @@ fn read_len(buf: &[u8]) -> Result { let end = checked_add(usize_try_from_i32(length)?, 4)?; if end < MIN_BSON_STRING_SIZE as usize { - return Err(Error::malformed_value(format!( + return Err(Error::malformed_bytes(format!( "BSON length encoded string needs to be at least {} bytes, instead got {}", MIN_BSON_STRING_SIZE, end ))); } if buf.len() < end { - return Err(Error::malformed_value(format!( + return Err(Error::malformed_bytes(format!( "expected buffer to contain at least {} bytes, but it only has {}", end, buf.len() @@ -263,7 +263,7 @@ fn read_len(buf: &[u8]) -> Result { } if buf[end - 1] != 0 { - return Err(Error::malformed_value( + return Err(Error::malformed_bytes( "expected string to be null-terminated", )); } @@ -283,16 +283,16 @@ fn read_lenencode(buf: &[u8]) -> Result<&str> { } fn try_to_str(data: &[u8]) -> Result<&str> { - simdutf8::basic::from_utf8(data).map_err(|_| ErrorKind::Utf8Encoding.into()) + simdutf8::basic::from_utf8(data).map_err(|_| ErrorKind::Utf8Encoding {}.into()) } fn usize_try_from_i32(i: i32) -> Result { - usize::try_from(i).map_err(Error::malformed_value) + usize::try_from(i).map_err(Error::malformed_bytes) } fn checked_add(lhs: usize, rhs: usize) -> Result { lhs.checked_add(rhs) - .ok_or_else(|| Error::malformed_value("attempted to add with overflow")) + .ok_or_else(|| Error::malformed_bytes("attempted to add with overflow")) } pub(crate) fn reader_to_vec(mut reader: R) -> Result> { @@ -301,7 +301,7 @@ pub(crate) fn reader_to_vec(mut reader: R) -> Result> { let length = i32::from_le_bytes(buf); if length < MIN_BSON_DOCUMENT_SIZE { - return Err(Error::malformed_value("document size too small")); + return Err(Error::malformed_bytes("document size too small")); } let mut bytes = Vec::with_capacity(length as usize); @@ -319,7 +319,7 @@ pub(crate) fn write_string(buf: &mut Vec, s: &str) { pub(crate) fn write_cstring(buf: &mut Vec, s: &str) -> Result<()> { if s.contains('\0') { - return Err(Error::malformed_value(format!( + return Err(Error::malformed_bytes(format!( "cstring with interior null: {:?}", s ))); diff --git a/src/raw/document.rs b/src/raw/document.rs index 262df7ec..4746d7c5 100644 --- a/src/raw/document.rs +++ b/src/raw/document.rs @@ -96,17 +96,17 @@ impl RawDocument { let data = data.as_ref(); if data.len() < 5 { - return Err(Error::malformed_value("document too short")); + return Err(Error::malformed_bytes("document too short")); } let length = i32_from_slice(data)?; if data.len() as i32 != length { - return Err(Error::malformed_value("document length incorrect")); + return Err(Error::malformed_bytes("document length incorrect")); } if data[data.len() - 1] != 0 { - return Err(Error::malformed_value("document not null-terminated")); + return Err(Error::malformed_bytes("document not null-terminated")); } Ok(RawDocument::new_unchecked(data)) @@ -497,11 +497,11 @@ impl RawDocument { let mut splits = buf.splitn(2, |x| *x == 0); let value = splits .next() - .ok_or_else(|| RawError::malformed_value("no value"))?; + .ok_or_else(|| RawError::malformed_bytes("no value"))?; if splits.next().is_some() { Ok(value) } else { - Err(RawError::malformed_value("expected null terminator")) + Err(RawError::malformed_bytes("expected null terminator")) } } diff --git a/src/raw/iter.rs b/src/raw/iter.rs index 6c3c640e..6355917e 100644 --- a/src/raw/iter.rs +++ b/src/raw/iter.rs @@ -80,7 +80,7 @@ impl<'a> RawIter<'a> { fn verify_enough_bytes(&self, start: usize, num_bytes: usize) -> Result<()> { let end = checked_add(start, num_bytes)?; if self.doc.as_bytes().get(start..end).is_none() { - return Err(Error::malformed_value(format!( + return Err(Error::malformed_bytes(format!( "length exceeds remaining length of buffer: {} vs {}", num_bytes, self.doc.as_bytes().len() - start @@ -94,7 +94,7 @@ impl<'a> RawIter<'a> { let size = i32_from_slice(&self.doc.as_bytes()[starting_at..])? as usize; if size < MIN_BSON_DOCUMENT_SIZE as usize { - return Err(Error::malformed_value(format!( + return Err(Error::malformed_bytes(format!( "document too small: {} bytes", size ))); @@ -103,7 +103,7 @@ impl<'a> RawIter<'a> { self.verify_enough_bytes(starting_at, size)?; if self.doc.as_bytes()[starting_at + size - 1] != 0 { - return Err(Error::malformed_value("not null terminated")); + return Err(Error::malformed_bytes("not null terminated")); } Ok(size) } @@ -317,7 +317,7 @@ impl<'a> RawElement<'a> { } fn malformed_error(&self, e: impl ToString) -> Error { - Error::malformed_value(e).with_key(self.key) + Error::malformed_bytes(e).with_key(self.key) } pub(crate) fn slice(&self) -> &'a [u8] { @@ -344,7 +344,7 @@ impl<'a> RawElement<'a> { Ok(ObjectId::from_bytes( self.doc.as_bytes()[start_at..(start_at + 12)] .try_into() - .map_err(|e| Error::malformed_value(e).with_key(self.key))?, + .map_err(|e| Error::malformed_bytes(e).with_key(self.key))?, )) } } @@ -353,7 +353,7 @@ impl RawIter<'_> { fn get_next_length_at(&self, start_at: usize) -> Result { let len = i32_from_slice(&self.doc.as_bytes()[start_at..])?; if len < 0 { - Err(Error::malformed_value("lengths can't be negative")) + Err(Error::malformed_bytes("lengths can't be negative")) } else { Ok(len as usize) } @@ -363,7 +363,7 @@ impl RawIter<'_> { let element_type = match ElementType::from(self.doc.as_bytes()[self.offset]) { Some(et) => et, None => { - return Err(Error::malformed_value(format!( + return Err(Error::malformed_bytes(format!( "invalid tag: {}", self.doc.as_bytes()[self.offset] ))); @@ -417,11 +417,11 @@ impl<'a> Iterator for RawIter<'a> { return None; } else { self.valid = false; - return Some(Err(Error::malformed_value("document not null terminated"))); + return Some(Err(Error::malformed_bytes("document not null terminated"))); } } else if self.offset >= self.doc.as_bytes().len() { self.valid = false; - return Some(Err(Error::malformed_value("iteration overflowed document"))); + return Some(Err(Error::malformed_bytes("iteration overflowed document"))); } let key = match self.doc.read_cstring_at(self.offset + 1) { diff --git a/src/raw/test.rs b/src/raw/test.rs index 0bc6db71..b651004d 100644 --- a/src/raw/test.rs +++ b/src/raw/test.rs @@ -16,13 +16,11 @@ use crate::{ #[test] fn test_decimal128_doesnt_panic_on_bad_codepoint_boundary() { - use crate::decimal128::ParseError; use std::str::FromStr; // idx 34 (Coefficient::MAX_DIGITS) on this string isn't a valid codepoint boundary - assert!(matches!( - Decimal128::from_str("111111111111111111111111111111111❤"), - Err(ParseError::Unparseable) - )) + assert!(Decimal128::from_str("111111111111111111111111111111111❤") + .unwrap_err() + .is_decimal128_unparseable()); } #[test] diff --git a/src/tests/modules/ser.rs b/src/tests/modules/ser.rs index 9eb839db..8e16f3b6 100644 --- a/src/tests/modules/ser.rs +++ b/src/tests/modules/ser.rs @@ -169,11 +169,11 @@ fn cstring_null_bytes_error() { let result = doc.encode_to_vec(); assert!(result.is_err(), "unexpected success"); let err = result.unwrap_err(); - assert!(err.is_malformed_value(), "unexpected error: {:?}", err); + assert!(err.is_malformed_bytes(), "unexpected error: {:?}", err); let result = serialize_to_vec(&doc); assert!(result.is_err(), "unexpected success"); match result.unwrap_err().strip_path() { - ser::Error::Crate(inner) if inner.is_malformed_value() => (), + ser::Error::Crate(inner) if inner.is_malformed_bytes() => (), err => panic!("unexpected error: {:?}", err), }; } diff --git a/src/uuid.rs b/src/uuid.rs index 61897b2e..70560361 100644 --- a/src/uuid.rs +++ b/src/uuid.rs @@ -137,7 +137,12 @@ mod test; use std::fmt::{self, Display}; -use crate::{spec::BinarySubtype, Binary, Bson}; +use crate::{ + error::{Error, Result}, + spec::BinarySubtype, + Binary, + Bson, +}; /// Special type name used in the [`Uuid`] serialization implementation to indicate a BSON /// UUID is being serialized or deserialized. The BSON serializers/deserializers will handle this @@ -185,9 +190,7 @@ impl Uuid { /// Creates a [`Uuid`] from the provided hex string. pub fn parse_str(input: impl AsRef) -> Result { - let uuid = uuid::Uuid::parse_str(input.as_ref()).map_err(|e| Error::InvalidUuidString { - message: e.to_string(), - })?; + let uuid = uuid::Uuid::parse_str(input.as_ref()).map_err(Error::invalid_uuid_string)?; Ok(Self::from_external_uuid(uuid)) } @@ -393,25 +396,23 @@ impl Binary { pub fn to_uuid_with_representation(&self, rep: UuidRepresentation) -> Result { // If representation is non-standard, then its subtype must be UuidOld if rep != UuidRepresentation::Standard && self.subtype != BinarySubtype::UuidOld { - return Err(Error::RepresentationMismatch { - requested_representation: rep, - actual_binary_subtype: self.subtype, - expected_binary_subtype: BinarySubtype::UuidOld, - }); + return Err(Error::uuid_representation_mismatch( + rep, + self.subtype, + BinarySubtype::UuidOld, + )); } // If representation is standard, then its subtype must be Uuid if rep == UuidRepresentation::Standard && self.subtype != BinarySubtype::Uuid { - return Err(Error::RepresentationMismatch { - requested_representation: rep, - actual_binary_subtype: self.subtype, - expected_binary_subtype: BinarySubtype::Uuid, - }); + return Err(Error::uuid_representation_mismatch( + rep, + self.subtype, + BinarySubtype::UuidOld, + )); } // Must be 16 bytes long if self.bytes.len() != 16 { - return Err(Error::InvalidLength { - length: self.bytes.len(), - }); + return Err(Error::invalid_uuid_length(self.bytes.len())); } let mut buf = [0u8; 16]; buf.copy_from_slice(&self.bytes); @@ -480,66 +481,3 @@ macro_rules! trait_impls { }; } trait_impls!(feature = "uuid-1", uuid::Uuid); - -/// Errors that can occur during [`Uuid`] construction and generation. -#[derive(Clone, Debug)] -#[non_exhaustive] -pub enum Error { - /// Error returned when an invalid string is provided to [`Uuid::parse_str`]. - #[non_exhaustive] - InvalidUuidString { message: String }, - - /// Error returned when the representation specified does not match the underlying - /// [`crate::Binary`] value in [`crate::Binary::to_uuid_with_representation`]. - #[non_exhaustive] - RepresentationMismatch { - /// The subtype that was expected given the requested representation. - expected_binary_subtype: BinarySubtype, - - /// The actual subtype of the binary value. - actual_binary_subtype: BinarySubtype, - - /// The requested representation. - requested_representation: UuidRepresentation, - }, - - /// Error returned from [`crate::Binary::to_uuid`] if the underling data is not 16 bytes long. - #[non_exhaustive] - InvalidLength { - /// The actual length of the data. - length: usize, - }, -} - -/// Alias for `Result`. -pub type Result = std::result::Result; - -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::InvalidUuidString { message } => { - write!(fmt, "{}", message) - } - Error::RepresentationMismatch { - expected_binary_subtype, - actual_binary_subtype, - requested_representation, - } => { - write!( - fmt, - "expected {:?} when converting to UUID with {:?}, isntead got {:?}", - expected_binary_subtype, requested_representation, actual_binary_subtype - ) - } - Error::InvalidLength { length } => { - write!( - fmt, - "expected UUID to contain 16 bytes, instead got {}", - length - ) - } - } - } -} - -impl std::error::Error for Error {}