From bd8272e227b23cf10ad4d504e514b89667822877 Mon Sep 17 00:00:00 2001 From: Isabel Atkinson Date: Thu, 5 Jun 2025 15:23:01 -0400 Subject: [PATCH 1/3] convert raw errors --- src/de/error.rs | 6 +- src/document.rs | 104 +++++++++++++++++------------------ src/error.rs | 94 ++++++++++++++++++++++--------- src/raw.rs | 33 ++++++----- src/raw/array.rs | 14 ++--- src/raw/document.rs | 41 +++++--------- src/raw/document_buf.rs | 8 +-- src/raw/error.rs | 72 ------------------------ src/raw/iter.rs | 119 ++++++++++++++++++++-------------------- 9 files changed, 220 insertions(+), 271 deletions(-) delete mode 100644 src/raw/error.rs diff --git a/src/de/error.rs b/src/de/error.rs index 8b3c7920..1d6eed0d 100644 --- a/src/de/error.rs +++ b/src/de/error.rs @@ -76,9 +76,9 @@ impl From for Error { } } -impl From for Error { - fn from(value: crate::raw::Error) -> Self { - Self::deserialization(value) +impl From for Error { + fn from(error: crate::error::Error) -> Self { + Self::deserialization(error.to_string()) } } diff --git a/src/document.rs b/src/document.rs index c4911e4a..30e49283 100644 --- a/src/document.rs +++ b/src/document.rs @@ -252,12 +252,12 @@ impl Document { match self.get(key) { Some(&Bson::Double(v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Double, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -267,12 +267,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::Double(ref mut v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Double, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a reference to the value for the given key if one is present and is of type @@ -282,12 +282,12 @@ impl Document { match self.get(key) { Some(Bson::Decimal128(v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Decimal128, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -297,12 +297,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::Decimal128(ref mut v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Decimal128, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a reference to the value for the given key if one is present and is of type @@ -310,14 +310,14 @@ impl Document { pub fn get_str(&self, key: impl AsRef) -> Result<&str> { let key = key.as_ref(); match self.get(key) { - Some(Bson::String(v)) => Ok(v), + Some(Bson::String(v)) => Ok(v.as_str()), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::String, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -325,14 +325,14 @@ impl Document { pub fn get_str_mut(&mut self, key: impl AsRef) -> Result<&mut str> { let key = key.as_ref(); match self.get_mut(key) { - Some(&mut Bson::String(ref mut v)) => Ok(v), + Some(&mut Bson::String(ref mut v)) => Ok(v.as_mut_str()), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::String, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a reference to the value for the given key if one is present and is of type @@ -342,12 +342,12 @@ impl Document { match self.get(key) { Some(Bson::Array(v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Array, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -357,12 +357,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::Array(ref mut v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Array, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a reference to the value for the given key if one is present and is of type @@ -372,12 +372,12 @@ impl Document { match self.get(key) { Some(Bson::Document(v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::EmbeddedDocument, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -387,12 +387,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::Document(ref mut v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::EmbeddedDocument, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a reference to the value for the given key if one is present and is of type @@ -402,12 +402,12 @@ impl Document { match self.get(key) { Some(&Bson::Boolean(v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Boolean, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -417,12 +417,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::Boolean(ref mut v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Boolean, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns [`Bson::Null`] if the given key corresponds to a [`Bson::Null`] value. @@ -431,12 +431,12 @@ impl Document { match self.get(key) { Some(&Bson::Null) => Ok(Bson::Null), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Null, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns the value for the given key if one is present and is of type [`ElementType::Int32`]. @@ -445,12 +445,12 @@ impl Document { match self.get(key) { Some(&Bson::Int32(v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Int32, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -460,12 +460,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::Int32(ref mut v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Int32, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns the value for the given key if one is present and is of type [`ElementType::Int64`]. @@ -474,12 +474,12 @@ impl Document { match self.get(key) { Some(&Bson::Int64(v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Int64, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -489,12 +489,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::Int64(ref mut v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Int64, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns the value for the given key if one is present and is of type @@ -504,12 +504,12 @@ impl Document { match self.get(key) { Some(&Bson::Timestamp(timestamp)) => Ok(timestamp), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Timestamp, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -519,12 +519,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::Timestamp(ref mut timestamp)) => Ok(timestamp), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Timestamp, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a reference to the value for the given key if one is present and is of type @@ -537,12 +537,12 @@ impl Document { ref bytes, })) => Ok(bytes), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Binary, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -555,12 +555,12 @@ impl Document { ref mut bytes, })) => Ok(bytes), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::Binary, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns the value for the given key if one is present and is of type @@ -570,12 +570,12 @@ impl Document { match self.get(key) { Some(&Bson::ObjectId(v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::ObjectId, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -585,12 +585,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::ObjectId(ref mut v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::ObjectId, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a reference to the value for the given key if one is present and is of type @@ -600,12 +600,12 @@ impl Document { match self.get(key) { Some(Bson::DateTime(v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::DateTime, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns a mutable reference to the value for the given key if one is present and is of type @@ -615,12 +615,12 @@ impl Document { match self.get_mut(key) { Some(&mut Bson::DateTime(ref mut v)) => Ok(v), Some(bson) => Err(Error::value_access_unexpected_type( - key, bson.element_type(), ElementType::DateTime, )), - None => Err(Error::value_access_not_present(key)), + None => Err(Error::value_access_not_present()), } + .map_err(|e| e.with_key(key)) } /// Returns whether the map contains a value for the specified key. diff --git a/src/error.rs b/src/error.rs index 5fefad1c..7b012fc7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,29 +6,62 @@ pub type Result = std::result::Result; /// An error that can occur in the `bson` crate. #[derive(Debug, Error)] -#[error("Kind: {kind}")] #[non_exhaustive] pub struct Error { /// The kind of error that occurred. pub kind: ErrorKind, + + /// The document key associated with the error, if any. + pub key: Option, + + /// The array index associated with the error, if any. + pub index: Option, +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(key) = self.key.as_deref() { + write!(f, "Error at key \"{key}\": ")?; + } else if let Some(index) = self.index { + write!(f, "Error at array index {index}: ")?; + } + + write!(f, "{}", self.kind) + } } /// The types of errors that can occur in the `bson` crate. #[derive(Debug, Error)] #[non_exhaustive] pub enum ErrorKind { + /// Malformed BSON bytes were encountered. + #[error("Malformed BSON: {message}")] + #[non_exhaustive] + MalformedValue { message: String }, + + /// Invalid UTF-8 bytes were encountered. + #[error("Invalid UTF-8")] + Utf8Encoding, + /// An error occurred when attempting to access a value in a document. - #[error("An error occurred when attempting to access a document value for key {key}: {kind}")] + #[error("An error occurred when attempting to access a document value: {kind}")] #[non_exhaustive] ValueAccess { - /// The key of the value. - key: String, - /// The kind of error that occurred. kind: ValueAccessErrorKind, }, } +impl From for Error { + fn from(kind: ErrorKind) -> Self { + Self { + kind, + key: None, + index: None, + } + } +} + /// The types of errors that can occur when attempting to access a value in a document. #[derive(Debug, Error)] #[non_exhaustive] @@ -55,35 +88,44 @@ pub enum ValueAccessErrorKind { } impl Error { - pub(crate) fn value_access_not_present(key: impl Into) -> Self { - Self { - kind: ErrorKind::ValueAccess { - key: key.into(), - kind: ValueAccessErrorKind::NotPresent, - }, + pub(crate) fn with_key(mut self, key: impl Into) -> Self { + self.key = Some(key.into()); + self + } + + pub(crate) fn with_index(mut self, index: usize) -> Self { + self.index = Some(index); + self + } + + pub(crate) fn value_access_not_present() -> Self { + ErrorKind::ValueAccess { + kind: ValueAccessErrorKind::NotPresent, } + .into() } - pub(crate) fn value_access_unexpected_type( - key: impl Into, - actual: ElementType, - expected: ElementType, - ) -> Self { - Self { - kind: ErrorKind::ValueAccess { - key: key.into(), - kind: ValueAccessErrorKind::UnexpectedType { actual, expected }, - }, + 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(key: impl Into, message: String) -> Self { - Self { - kind: ErrorKind::ValueAccess { - key: key.into(), - kind: ValueAccessErrorKind::InvalidBson { message }, + pub(crate) fn value_access_invalid_bson(message: String) -> Self { + ErrorKind::ValueAccess { + kind: ValueAccessErrorKind::InvalidBson { + message: message.into(), }, } + .into() + } + + pub(crate) fn malformed_value(message: impl ToString) -> Self { + ErrorKind::MalformedValue { + message: message.to_string(), + } + .into() } #[cfg(test)] diff --git a/src/raw.rs b/src/raw.rs index 364e45d5..1851dd5a 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -118,7 +118,6 @@ mod bson; mod bson_ref; mod document; mod document_buf; -mod error; mod iter; pub(crate) mod serde; #[cfg(test)] @@ -126,7 +125,10 @@ mod test; use std::convert::{TryFrom, TryInto}; -use crate::de::MIN_BSON_STRING_SIZE; +use crate::{ + de::MIN_BSON_STRING_SIZE, + error::{Error, ErrorKind, Result}, +}; pub use self::{ array::{RawArray, RawArrayIter}, @@ -141,7 +143,6 @@ pub use self::{ }, document::RawDocument, document_buf::{BindRawBsonRef, RawDocumentBuf}, - error::{Error, ErrorKind, Result}, iter::{RawElement, RawIter}, }; @@ -163,7 +164,7 @@ fn f64_from_slice(val: &[u8]) -> Result { .get(0..8) .and_then(|s| s.try_into().ok()) .ok_or_else(|| { - Error::malformed(format!( + Error::malformed_value(format!( "expected 8 bytes to read double, instead got {}", val.len() )) @@ -178,7 +179,7 @@ fn i32_from_slice(val: &[u8]) -> Result { .get(0..4) .and_then(|s| s.try_into().ok()) .ok_or_else(|| { - Error::malformed(format!( + Error::malformed_value(format!( "expected 4 bytes to read i32, instead got {}", val.len() )) @@ -193,7 +194,7 @@ fn i64_from_slice(val: &[u8]) -> Result { .get(0..8) .and_then(|s| s.try_into().ok()) .ok_or_else(|| { - Error::malformed(format!( + Error::malformed_value(format!( "expected 8 bytes to read i64, instead got {}", val.len() )) @@ -206,7 +207,7 @@ fn u8_from_slice(val: &[u8]) -> Result { .get(0..1) .and_then(|s| s.try_into().ok()) .ok_or_else(|| { - Error::malformed(format!( + Error::malformed_value(format!( "expected 1 byte to read u8, instead got {}", val.len() )) @@ -217,7 +218,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(format!( + return Err(Error::malformed_value(format!( "boolean must be stored as 0 or 1, got {}", val ))); @@ -228,7 +229,7 @@ pub(crate) fn bool_from_slice(val: &[u8]) -> Result { fn read_len(buf: &[u8]) -> Result { if buf.len() < 4 { - return Err(Error::malformed(format!( + return Err(Error::malformed_value(format!( "expected buffer with string to contain at least 4 bytes, but it only has {}", buf.len() ))); @@ -238,14 +239,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(format!( + return Err(Error::malformed_value(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(format!( + return Err(Error::malformed_value(format!( "expected buffer to contain at least {} bytes, but it only has {}", end, buf.len() @@ -253,7 +254,9 @@ fn read_len(buf: &[u8]) -> Result { } if buf[end - 1] != 0 { - return Err(Error::malformed("expected string to be null-terminated")); + return Err(Error::malformed_value( + "expected string to be null-terminated", + )); } Ok(length as usize + 4) @@ -271,14 +274,14 @@ fn read_lenencode(buf: &[u8]) -> Result<&str> { } fn try_to_str(data: &[u8]) -> Result<&str> { - simdutf8::basic::from_utf8(data).map_err(|_| Error::new(ErrorKind::Utf8EncodingError)) + 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) + usize::try_from(i).map_err(Error::malformed_value) } fn checked_add(lhs: usize, rhs: usize) -> Result { lhs.checked_add(rhs) - .ok_or_else(|| Error::malformed("attempted to add with overflow")) + .ok_or_else(|| Error::malformed_value("attempted to add with overflow")) } diff --git a/src/raw/array.rs b/src/raw/array.rs index ce2232eb..4f2f47ba 100644 --- a/src/raw/array.rs +++ b/src/raw/array.rs @@ -115,15 +115,15 @@ impl RawArray { ) -> Result { let bson = self .get(index) - .map_err(|e| Error::value_access_invalid_bson(index.to_string(), format!("{:?}", e)))? - .ok_or_else(|| Error::value_access_not_present(index.to_string()))?; + .map_err(|e| Error::value_access_invalid_bson(format!("{:?}", e)))? + .ok_or_else(Error::value_access_not_present) + .map_err(|e| e.with_index(index))?; match f(bson) { Some(t) => Ok(t), - None => Err(Error::value_access_unexpected_type( - index.to_string(), - bson.element_type(), - expected_type, - )), + None => Err( + Error::value_access_unexpected_type(bson.element_type(), expected_type) + .with_index(index), + ), } } diff --git a/src/raw/document.rs b/src/raw/document.rs index c39276f3..8f1cbfaf 100644 --- a/src/raw/document.rs +++ b/src/raw/document.rs @@ -8,7 +8,7 @@ use serde::{ser::SerializeMap, Deserialize, Serialize}; use crate::{ de::MIN_BSON_DOCUMENT_SIZE, error::{Error, Result}, - raw::{error::ErrorKind, serde::OwnedOrBorrowedRawDocument, RAW_DOCUMENT_NEWTYPE}, + raw::{serde::OwnedOrBorrowedRawDocument, RAW_DOCUMENT_NEWTYPE}, DateTime, Timestamp, }; @@ -95,32 +95,17 @@ impl RawDocument { let data = data.as_ref(); if data.len() < 5 { - return Err(RawError { - key: None, - kind: ErrorKind::MalformedValue { - message: "document too short".into(), - }, - }); + return Err(Error::malformed_value("document too short")); } let length = i32_from_slice(data)?; if data.len() as i32 != length { - return Err(RawError { - key: None, - kind: ErrorKind::MalformedValue { - message: "document length incorrect".into(), - }, - }); + return Err(Error::malformed_value("document length incorrect")); } if data[data.len() - 1] != 0 { - return Err(RawError { - key: None, - kind: ErrorKind::MalformedValue { - message: "document not null-terminated".into(), - }, - }); + return Err(Error::malformed_value("document not null-terminated")); } Ok(RawDocument::new_unchecked(data)) @@ -211,15 +196,15 @@ impl RawDocument { let bson = self .get(key) - .map_err(|e| Error::value_access_invalid_bson(key, format!("{:?}", e)))? - .ok_or_else(|| Error::value_access_not_present(key))?; + .map_err(|e| Error::value_access_invalid_bson(format!("{:?}", e)))? + .ok_or_else(Error::value_access_not_present) + .map_err(|e| e.with_key(key))?; match f(bson) { Some(t) => Ok(t), - None => Err(Error::value_access_unexpected_type( - key, - bson.element_type(), - expected_type, - )), + None => Err( + Error::value_access_unexpected_type(bson.element_type(), expected_type) + .with_key(key), + ), } } @@ -511,11 +496,11 @@ impl RawDocument { let mut splits = buf.splitn(2, |x| *x == 0); let value = splits .next() - .ok_or_else(|| RawError::malformed("no value"))?; + .ok_or_else(|| RawError::malformed_value("no value"))?; if splits.next().is_some() { Ok(value) } else { - Err(RawError::malformed("expected null terminator")) + Err(RawError::malformed_value("expected null terminator")) } } diff --git a/src/raw/document_buf.rs b/src/raw/document_buf.rs index 7a3e0dca..bdcf83ca 100644 --- a/src/raw/document_buf.rs +++ b/src/raw/document_buf.rs @@ -14,7 +14,6 @@ use super::{ iter::Iter, serde::OwnedOrBorrowedRawDocument, Error, - ErrorKind, RawBsonRef, RawDocument, RawIter, @@ -111,12 +110,7 @@ impl RawDocumentBuf { /// ``` pub fn from_document(doc: &Document) -> Result { let mut data = Vec::new(); - doc.to_writer(&mut data).map_err(|e| Error { - key: None, - kind: ErrorKind::MalformedValue { - message: e.to_string(), - }, - })?; + doc.to_writer(&mut data).map_err(Error::malformed_value)?; Ok(Self { data }) } diff --git a/src/raw/error.rs b/src/raw/error.rs deleted file mode 100644 index 10f3a8f6..00000000 --- a/src/raw/error.rs +++ /dev/null @@ -1,72 +0,0 @@ -/// An error that occurs when attempting to parse raw BSON bytes. -#[derive(Debug, PartialEq, Clone)] -#[non_exhaustive] -pub struct Error { - /// The type of error that was encountered. - pub kind: ErrorKind, - - /// They key associated with the error, if any. - pub(crate) key: Option, -} - -impl Error { - pub(crate) fn new(kind: ErrorKind) -> Self { - Self { key: None, kind } - } - - pub(crate) fn malformed(e: impl ToString) -> Self { - Self::new(ErrorKind::MalformedValue { - message: e.to_string(), - }) - } - - pub(crate) fn with_key(mut self, key: impl Into) -> Self { - self.key = Some(key.into()); - self - } - - /// The key at which the error was encountered, if any. - pub fn key(&self) -> Option<&str> { - self.key.as_deref() - } -} - -/// The different categories of errors that can be returned when reading from raw BSON. -#[derive(Clone, Debug, PartialEq)] -#[non_exhaustive] -pub enum ErrorKind { - /// A BSON value did not fit the proper format. - #[non_exhaustive] - MalformedValue { message: String }, - - /// Improper UTF-8 bytes were found when proper UTF-8 was expected. - Utf8EncodingError, -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let p = self - .key - .as_ref() - .map(|k| format!("error at key \"{}\": ", k)); - - let prefix = p.as_ref().map_or("", |p| p.as_str()); - - match &self.kind { - ErrorKind::MalformedValue { message } => { - write!(f, "{}malformed value: {:?}", prefix, message) - } - ErrorKind::Utf8EncodingError => write!(f, "{}utf-8 encoding error", prefix), - } - } -} - -impl std::error::Error for Error {} - -pub type Result = std::result::Result; - -/// Execute the provided closure, mapping the key of the returned error (if any) to the provided -/// key. -pub(crate) fn try_with_key Result>(key: impl Into, f: F) -> Result { - f().map_err(|e| e.with_key(key)) -} diff --git a/src/raw/iter.rs b/src/raw/iter.rs index 9082dada..9a4150dc 100644 --- a/src/raw/iter.rs +++ b/src/raw/iter.rs @@ -20,7 +20,6 @@ use crate::{ use super::{ bool_from_slice, checked_add, - error::try_with_key, f64_from_slice, i32_from_slice, i64_from_slice, @@ -82,7 +81,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(format!( + return Err(Error::malformed_value(format!( "length exceeds remaining length of buffer: {} vs {}", num_bytes, self.doc.as_bytes().len() - start @@ -96,7 +95,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(format!( + return Err(Error::malformed_value(format!( "document too small: {} bytes", size ))); @@ -105,7 +104,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("not null terminated")); + return Err(Error::malformed_value("not null terminated")); } Ok(size) } @@ -308,7 +307,7 @@ impl<'a> RawElement<'a> { } fn malformed_error(&self, e: impl ToString) -> Error { - Error::malformed(e).with_key(self.key) + Error::malformed_value(e).with_key(self.key) } pub(crate) fn slice(&self) -> &'a [u8] { @@ -335,7 +334,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(e).with_key(self.key))?, + .map_err(|e| Error::malformed_value(e).with_key(self.key))?, )) } } @@ -344,11 +343,56 @@ 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("lengths can't be negative")) + Err(Error::malformed_value("lengths can't be negative")) } else { Ok(len as usize) } } + + fn get_next_kvp(&mut self, offset: usize) -> Result<(ElementType, usize)> { + let element_type = match ElementType::from(self.doc.as_bytes()[self.offset]) { + Some(et) => et, + None => { + return Err(Error::malformed_value(format!( + "invalid tag: {}", + self.doc.as_bytes()[self.offset] + ))); + } + }; + + let element_size = match element_type { + ElementType::Boolean => 1, + ElementType::Int32 => 4, + ElementType::Int64 => 8, + ElementType::Double => 8, + ElementType::DateTime => 8, + ElementType::Timestamp => 8, + ElementType::ObjectId => 12, + ElementType::Decimal128 => 16, + ElementType::Null => 0, + ElementType::Undefined => 0, + ElementType::MinKey => 0, + ElementType::MaxKey => 0, + ElementType::String => read_len(&self.doc.as_bytes()[offset..])?, + ElementType::EmbeddedDocument => self.next_document_len(offset)?, + ElementType::Array => self.next_document_len(offset)?, + ElementType::Binary => self.get_next_length_at(offset)? + 4 + 1, + ElementType::RegularExpression => { + let pattern = self.doc.read_cstring_at(offset)?; + let options = self.doc.read_cstring_at(offset + pattern.len() + 1)?; + pattern.len() + 1 + options.len() + 1 + } + ElementType::DbPointer => read_len(&self.doc.as_bytes()[offset..])? + 12, + ElementType::Symbol => read_len(&self.doc.as_bytes()[offset..])?, + ElementType::JavaScriptCode => read_len(&self.doc.as_bytes()[offset..])?, + ElementType::JavaScriptCodeWithScope => self.get_next_length_at(offset)?, + }; + + self.verify_enough_bytes(offset, element_size)?; + self.offset = offset + element_size; + + Ok((element_type, element_size)) + } } impl<'a> Iterator for RawIter<'a> { @@ -363,11 +407,11 @@ impl<'a> Iterator for RawIter<'a> { return None; } else { self.valid = false; - return Some(Err(Error::malformed("document not null terminated"))); + return Some(Err(Error::malformed_value("document not null terminated"))); } } else if self.offset >= self.doc.as_bytes().len() { self.valid = false; - return Some(Err(Error::malformed("iteration overflowed document"))); + return Some(Err(Error::malformed_value("iteration overflowed document"))); } let key = match self.doc.read_cstring_at(self.offset + 1) { @@ -377,59 +421,9 @@ impl<'a> Iterator for RawIter<'a> { return Some(Err(e)); } }; - let offset = self.offset + 1 + key.len() + 1; // type specifier + key + \0 - let kvp_result = try_with_key(key, || { - let element_type = match ElementType::from(self.doc.as_bytes()[self.offset]) { - Some(et) => et, - None => { - return Err(Error::malformed(format!( - "invalid tag: {}", - self.doc.as_bytes()[self.offset] - )) - .with_key(key)) - } - }; - - let element_size = match element_type { - ElementType::Boolean => 1, - ElementType::Int32 => 4, - ElementType::Int64 => 8, - ElementType::Double => 8, - ElementType::DateTime => 8, - ElementType::Timestamp => 8, - ElementType::ObjectId => 12, - ElementType::Decimal128 => 16, - ElementType::Null => 0, - ElementType::Undefined => 0, - ElementType::MinKey => 0, - ElementType::MaxKey => 0, - ElementType::String => read_len(&self.doc.as_bytes()[offset..])?, - ElementType::EmbeddedDocument => self.next_document_len(offset)?, - ElementType::Array => self.next_document_len(offset)?, - ElementType::Binary => self.get_next_length_at(offset)? + 4 + 1, - ElementType::RegularExpression => { - let pattern = self.doc.read_cstring_at(offset)?; - let options = self.doc.read_cstring_at(offset + pattern.len() + 1)?; - pattern.len() + 1 + options.len() + 1 - } - ElementType::DbPointer => read_len(&self.doc.as_bytes()[offset..])? + 12, - ElementType::Symbol => read_len(&self.doc.as_bytes()[offset..])?, - ElementType::JavaScriptCode => read_len(&self.doc.as_bytes()[offset..])?, - ElementType::JavaScriptCodeWithScope => self.get_next_length_at(offset)?, - }; - - self.verify_enough_bytes(offset, element_size)?; - self.offset = offset + element_size; - - Ok((element_type, element_size)) - }); - if kvp_result.is_err() { - self.valid = false; - } - - Some(match kvp_result { + Some(match self.get_next_kvp(offset) { Ok((kind, size)) => Ok(RawElement { key, kind, @@ -437,7 +431,10 @@ impl<'a> Iterator for RawIter<'a> { start_at: offset, size, }), - Err(e) => Err(e), + Err(error) => { + self.valid = false; + Err(error.with_key(key)) + } }) } } From 5d281df11148006f0ddca3c309877bbc556a5cfc Mon Sep 17 00:00:00 2001 From: Isabel Atkinson Date: Thu, 5 Jun 2025 15:26:14 -0400 Subject: [PATCH 2/3] clippy --- src/error.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index 7b012fc7..58f3e000 100644 --- a/src/error.rs +++ b/src/error.rs @@ -114,9 +114,7 @@ impl Error { pub(crate) fn value_access_invalid_bson(message: String) -> Self { ErrorKind::ValueAccess { - kind: ValueAccessErrorKind::InvalidBson { - message: message.into(), - }, + kind: ValueAccessErrorKind::InvalidBson { message }, } .into() } From aad9a4da7cd3fd2444bbd5d13627cbdcd5dea68b Mon Sep 17 00:00:00 2001 From: Isabel Atkinson Date: Thu, 5 Jun 2025 15:33:05 -0400 Subject: [PATCH 3/3] fix docs --- src/raw.rs | 4 ++-- src/raw/array_buf.rs | 4 ++-- src/raw/document.rs | 28 ++++++++++++++-------------- src/raw/document_buf.rs | 17 +++++++---------- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/raw.rs b/src/raw.rs index 1851dd5a..252832c2 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -30,7 +30,7 @@ //! elem.as_str(), //! Some("y'all"), //! ); -//! # Ok::<(), bson::raw::Error>(()) +//! # Ok::<(), bson::error::Error>(()) //! ``` //! //! ### [`crate::Document`] interop @@ -109,7 +109,7 @@ //! let (key, value): (&str, RawBsonRef) = doc_iter.next().unwrap()?; //! assert_eq!(key, "year"); //! assert_eq!(value.as_str(), Some("2021")); -//! # Ok::<(), bson::raw::Error>(()) +//! # Ok::<(), bson::error::Error>(()) //! ``` mod array; diff --git a/src/raw/array_buf.rs b/src/raw/array_buf.rs index d9969d99..b8ac1f06 100644 --- a/src/raw/array_buf.rs +++ b/src/raw/array_buf.rs @@ -17,7 +17,7 @@ use super::{document_buf::BindRawBsonRef, serde::OwnedOrBorrowedRawArray, RawArr /// Iterating over a [`RawArrayBuf`] yields either an error or a [`RawBson`](crate::raw::RawBson) /// value that borrows from the original document without making any additional allocations. /// ``` -/// # use bson::raw::Error; +/// # use bson::error::Error; /// use bson::raw::RawArrayBuf; /// /// let mut array = RawArrayBuf::new(); @@ -67,7 +67,7 @@ impl RawArrayBuf { /// Append a value to the end of the array. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::raw::{RawArrayBuf, RawDocumentBuf}; /// /// let mut array = RawArrayBuf::new(); diff --git a/src/raw/document.rs b/src/raw/document.rs index 8f1cbfaf..468a3da0 100644 --- a/src/raw/document.rs +++ b/src/raw/document.rs @@ -42,7 +42,7 @@ use crate::{oid::ObjectId, spec::ElementType, Document}; /// Iterating over a [`RawDocument`] yields either an error or a key-value pair that borrows from /// the original document without making any additional allocations. /// ``` -/// # use bson::raw::{Error}; +/// # use bson::error::Error; /// use bson::raw::RawDocument; /// /// let doc = RawDocument::from_bytes(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00")?; @@ -89,7 +89,7 @@ impl RawDocument { /// use bson::raw::RawDocument; /// /// let doc = RawDocument::from_bytes(b"\x05\0\0\0\0")?; - /// # Ok::<(), bson::raw::Error>(()) + /// # Ok::<(), bson::error::Error>(()) /// ``` pub fn from_bytes + ?Sized>(data: &D) -> RawResult<&RawDocument> { let data = data.as_ref(); @@ -128,12 +128,12 @@ impl RawDocument { /// Creates a new [`RawDocumentBuf`] with an owned copy of the BSON bytes. /// /// ``` - /// use bson::raw::{RawDocument, RawDocumentBuf, Error}; + /// use bson::raw::{RawDocument, RawDocumentBuf}; /// /// let data = b"\x05\0\0\0\0"; /// let doc_ref = RawDocument::from_bytes(data)?; /// let doc: RawDocumentBuf = doc_ref.to_raw_document_buf(); - /// # Ok::<(), Error>(()) + /// # Ok::<(), bson::error::Error>(()) pub fn to_raw_document_buf(&self) -> RawDocumentBuf { // unwrap is ok here because we already verified the bytes in `RawDocumentRef::new` RawDocumentBuf::from_bytes(self.data.to_owned()).unwrap() @@ -143,7 +143,7 @@ impl RawDocument { /// found. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::{rawdoc, oid::ObjectId}; /// /// let doc = rawdoc! { @@ -212,7 +212,7 @@ impl RawDocument { /// if the key corresponds to a value which isn't a double. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::rawdoc; /// /// let doc = rawdoc! { @@ -253,7 +253,7 @@ impl RawDocument { /// the key corresponds to a value which isn't a document. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::rawdoc; /// /// let doc = rawdoc! { @@ -322,7 +322,7 @@ impl RawDocument { /// the key corresponds to a value which isn't an ObjectId. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::{rawdoc, oid::ObjectId}; /// /// let doc = rawdoc! { @@ -343,7 +343,7 @@ impl RawDocument { /// the key corresponds to a value which isn't a boolean. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::{rawdoc, oid::ObjectId}; /// /// let doc = rawdoc! { @@ -364,7 +364,7 @@ impl RawDocument { /// error if the key corresponds to a value which isn't a DateTime. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::{rawdoc, DateTime}; /// /// let dt = DateTime::now(); @@ -410,7 +410,7 @@ impl RawDocument { /// error if the key corresponds to a value which isn't a timestamp. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::{rawdoc, Timestamp}; /// /// let doc = rawdoc! { @@ -434,7 +434,7 @@ impl RawDocument { /// the key corresponds to a value which isn't a 32-bit integer. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::rawdoc; /// /// let doc = rawdoc! { @@ -455,7 +455,7 @@ impl RawDocument { /// the key corresponds to a value which isn't a 64-bit integer. /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::rawdoc; /// /// let doc = rawdoc! { @@ -475,7 +475,7 @@ impl RawDocument { /// Return a reference to the contained data as a `&[u8]` /// /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::rawdoc; /// let docbuf = rawdoc! {}; /// assert_eq!(docbuf.as_bytes(), b"\x05\x00\x00\x00\x00"); diff --git a/src/raw/document_buf.rs b/src/raw/document_buf.rs index bdcf83ca..07b0fdae 100644 --- a/src/raw/document_buf.rs +++ b/src/raw/document_buf.rs @@ -33,7 +33,7 @@ mod raw_writer; /// the original document without making any additional allocations. /// /// ``` -/// # use bson::raw::Error; +/// # use bson::error::Error; /// use bson::raw::RawDocumentBuf; /// /// let doc = RawDocumentBuf::from_bytes(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?; @@ -85,9 +85,9 @@ impl RawDocumentBuf { /// the RawDocument will return Errors where appropriate. /// /// ``` - /// # use bson::raw::{RawDocumentBuf, Error}; + /// # use bson::raw::RawDocumentBuf; /// let doc = RawDocumentBuf::from_bytes(b"\x05\0\0\0\0".to_vec())?; - /// # Ok::<(), Error>(()) + /// # Ok::<(), bson::error::Error>(()) /// ``` pub fn from_bytes(data: Vec) -> Result { let _ = RawDocument::from_bytes(data.as_slice())?; @@ -97,7 +97,6 @@ impl RawDocumentBuf { /// Create a [`RawDocumentBuf`] from a [`Document`]. /// /// ``` - /// # use bson::raw::Error; /// use bson::{doc, oid::ObjectId, raw::RawDocumentBuf}; /// /// let document = doc! { @@ -106,7 +105,7 @@ impl RawDocumentBuf { /// "title": "Moby-Dick", /// }; /// let doc = RawDocumentBuf::from_document(&document)?; - /// # Ok::<(), Error>(()) + /// # Ok::<(), bson::error::Error>(()) /// ``` pub fn from_document(doc: &Document) -> Result { let mut data = Vec::new(); @@ -119,7 +118,6 @@ impl RawDocumentBuf { /// `Result<(&str, RawBson<'_>)>`. /// /// ``` - /// # use bson::raw::Error; /// use bson::{doc, raw::RawDocumentBuf}; /// /// let doc = RawDocumentBuf::from_document(&doc! { "ferris": true })?; @@ -129,7 +127,7 @@ impl RawDocumentBuf { /// assert_eq!(key, "ferris"); /// assert_eq!(value.as_bool(), Some(true)); /// } - /// # Ok::<(), Error>(()) + /// # Ok::<(), bson::error::Error>(()) /// ``` /// /// # Note: @@ -164,12 +162,11 @@ impl RawDocumentBuf { /// Return the contained data as a `Vec` /// /// ``` - /// # use bson::raw::Error; /// use bson::{doc, raw::RawDocumentBuf}; /// /// let doc = RawDocumentBuf::from_document(&doc!{})?; /// assert_eq!(doc.into_bytes(), b"\x05\x00\x00\x00\x00".to_vec()); - /// # Ok::<(), Error>(()) + /// # Ok::<(), bson::error::Error>(()) /// ``` pub fn into_bytes(self) -> Vec { self.data @@ -186,7 +183,7 @@ impl RawDocumentBuf { /// Values can be any type that can be converted to either borrowed or owned raw bson data; see /// the documentation for [BindRawBsonRef] for more details. /// ``` - /// # use bson::raw::Error; + /// # use bson::error::Error; /// use bson::{doc, raw::{RawBsonRef, RawDocumentBuf}}; /// /// let mut doc = RawDocumentBuf::new();