Skip to content
41 changes: 10 additions & 31 deletions src/binary.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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)?;
Expand All @@ -50,9 +55,7 @@ impl Binary {
input: impl AsRef<str>,
subtype: impl Into<Option<BinarySubtype>>,
) -> Result<Self> {
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,
Expand Down Expand Up @@ -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<T> = std::result::Result<T, Error>;
59 changes: 26 additions & 33 deletions src/binary/vector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)?;
Expand All @@ -91,17 +91,14 @@ impl PackedBitVector {
pub fn new(vector: Vec<u8>, padding: impl Into<Option<u8>>) -> Result<Self> {
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 })
}
Expand All @@ -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..];

Expand All @@ -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));
}
Expand All @@ -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}"
))),
}
}

Expand Down Expand Up @@ -228,9 +220,10 @@ impl TryFrom<&Binary> for Vector {

fn try_from(binary: &Binary) -> Result<Self> {
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)
}
Expand Down
50 changes: 7 additions & 43 deletions src/datetime.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
//! 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(
feature = "serde_with-3",
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.
Expand Down Expand Up @@ -388,21 +387,13 @@ impl crate::DateTime {

/// Convert this [`DateTime`] to an RFC 3339 formatted string.
pub fn try_to_rfc3339_string(self) -> Result<String> {
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<str>) -> Result<Self> {
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))
}

Expand Down Expand Up @@ -543,30 +534,3 @@ impl serde_with::SerializeAs<time::OffsetDateTime> 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<T, DateTime::Error>`
pub type Result<T> = result::Result<T, Error>;

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 {}
16 changes: 9 additions & 7 deletions src/datetime/builder.rs
Original file line number Diff line number Diff line change
@@ -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<Y = NoYear, M = NoMonth, D = NoDay> {
pub(crate) year: Y,
Expand Down Expand Up @@ -169,19 +174,16 @@ impl DateTimeBuilder<Year, Month, Day> {
///
/// Note: You cannot call `build()` before setting at least the year, month and day.
pub fn build(self) -> Result<DateTime> {
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()))
}
}
Loading