From 97dc82b5e8e67d9d767e53c9baa1a03682abbca9 Mon Sep 17 00:00:00 2001 From: marcelbuesing Date: Tue, 3 Jul 2018 22:24:38 +0200 Subject: [PATCH] Add i2u feature (#94) --- .travis.yml | 1 + Cargo.toml | 8 +++++ src/encoder/error.rs | 11 ++++++ src/encoder/serde.rs | 36 +++++++++++++++----- src/lib.rs | 1 + tests/lib.rs | 2 ++ tests/modules/ser.rs | 79 ++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 128 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index a28e4155..6e6daa6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,5 @@ sudo: false script: - cargo build -v - cargo test -v --no-fail-fast + - cargo test -v --no-fail-fast --features u2i - cd serde-tests && cargo test -v --no-fail-fast diff --git a/Cargo.toml b/Cargo.toml index 27468d15..0a333b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,12 @@ readme = "README.md" homepage = "https://github.com/zonyitoo/bson-rs" documentation = "https://docs.rs/crate/bson" +[features] +# no features by default +default = [] +# attempt to encode unsigned types in signed types +u2i = [] + [lib] name = "bson" @@ -26,7 +32,9 @@ linked-hash-map = "0.5" hostname = "0.1" hex = "0.3" md5 = "0.3" +try_from = "0.2" [dev-dependencies] +assert_matches = "1.2" serde_derive = "1.0" serde_bytes = "0.10" diff --git a/src/encoder/error.rs b/src/encoder/error.rs index 7b79650f..2fe48914 100644 --- a/src/encoder/error.rs +++ b/src/encoder/error.rs @@ -10,6 +10,7 @@ pub enum EncoderError { InvalidMapKeyType(Bson), Unknown(String), UnsupportedUnsignedType, + UnsignedTypesValueExceedsRange(u64), } impl From for EncoderError { @@ -25,6 +26,14 @@ impl fmt::Display for EncoderError { &EncoderError::InvalidMapKeyType(ref bson) => write!(fmt, "Invalid map key type: {:?}", bson), &EncoderError::Unknown(ref inner) => inner.fmt(fmt), &EncoderError::UnsupportedUnsignedType => write!(fmt, "BSON does not support unsigned type"), + &EncoderError::UnsignedTypesValueExceedsRange(value) => { + write!( + fmt, + "BSON does not support unsigned types. + An attempt to encode the value: {} in a signed type failed due to the values size.", + value + ) + }, } } } @@ -36,6 +45,8 @@ impl error::Error for EncoderError { &EncoderError::InvalidMapKeyType(_) => "Invalid map key type", &EncoderError::Unknown(ref inner) => inner, &EncoderError::UnsupportedUnsignedType => "BSON does not support unsigned type", + &EncoderError::UnsignedTypesValueExceedsRange(_) => "BSON does not support unsigned types. + An attempt to encode the value: {} in a signed type failed due to the values size." } } fn cause(&self) -> Option<&error::Error> { diff --git a/src/encoder/serde.rs b/src/encoder/serde.rs index aa037c48..deeef74e 100644 --- a/src/encoder/serde.rs +++ b/src/encoder/serde.rs @@ -3,6 +3,7 @@ use serde::ser::{Serialize, SerializeMap, SerializeSeq, SerializeStruct, Seriali use bson::{Array, Bson, Document, UtcDateTime}; use oid::ObjectId; +use try_from::TryFrom; use super::{to_bson, EncoderError, EncoderResult}; @@ -88,8 +89,12 @@ impl Serializer for Encoder { } #[inline] - fn serialize_u8(self, _value: u8) -> EncoderResult { - Err(EncoderError::UnsupportedUnsignedType) + fn serialize_u8(self, value: u8) -> EncoderResult { + if cfg!(feature = "u2i") { + Ok(Bson::I32(value as i32)) + } else { + Err(EncoderError::UnsupportedUnsignedType) + } } #[inline] @@ -98,8 +103,12 @@ impl Serializer for Encoder { } #[inline] - fn serialize_u16(self, _value: u16) -> EncoderResult { - Err(EncoderError::UnsupportedUnsignedType) + fn serialize_u16(self, value: u16) -> EncoderResult { + if cfg!(feature = "u2i") { + Ok(Bson::I32(value as i32)) + } else { + Err(EncoderError::UnsupportedUnsignedType) + } } #[inline] @@ -108,8 +117,12 @@ impl Serializer for Encoder { } #[inline] - fn serialize_u32(self, _value: u32) -> EncoderResult { - Err(EncoderError::UnsupportedUnsignedType) + fn serialize_u32(self, value: u32) -> EncoderResult { + if cfg!(feature = "u2i") { + Ok(Bson::I64(value as i64)) + } else { + Err(EncoderError::UnsupportedUnsignedType) + } } #[inline] @@ -118,8 +131,15 @@ impl Serializer for Encoder { } #[inline] - fn serialize_u64(self, _value: u64) -> EncoderResult { - Err(EncoderError::UnsupportedUnsignedType) + fn serialize_u64(self, value: u64) -> EncoderResult { + if cfg!(feature = "u2i") { + match i64::try_from(value) { + Ok(ivalue) => Ok(Bson::I64(ivalue)), + Err(_) => Err(EncoderError::UnsignedTypesValueExceedsRange(value)), + } + } else { + Err(EncoderError::UnsupportedUnsignedType) + } } #[inline] diff --git a/src/lib.rs b/src/lib.rs index 88660534..c7b672e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,7 @@ extern crate serde; extern crate serde_json; extern crate md5; extern crate time; +extern crate try_from; pub use self::bson::{Array, Bson, Document, TimeStamp, UtcDateTime}; pub use self::decoder::{decode_document, decode_document_utf8_lossy, from_bson, Decoder, DecoderError, DecoderResult}; diff --git a/tests/lib.rs b/tests/lib.rs index 7d1f2b15..5e09b168 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,3 +1,5 @@ +#[macro_use(assert_matches)] +extern crate assert_matches; #[macro_use(bson, doc)] extern crate bson; extern crate byteorder; diff --git a/tests/modules/ser.rs b/tests/modules/ser.rs index 74d07cb2..b42b5ad5 100644 --- a/tests/modules/ser.rs +++ b/tests/modules/ser.rs @@ -1,6 +1,7 @@ -use bson::{from_bson, to_bson, Bson}; +use bson::{from_bson, to_bson, Bson, EncoderError, EncoderResult}; use bson::oid::ObjectId; use std::collections::BTreeMap; +use std::{u8, u16, u32, u64}; #[test] fn floating_point() { @@ -53,11 +54,85 @@ fn int32() { assert_eq!(deser, obj); } +#[test] +#[cfg_attr(feature = "u2i", ignore)] +fn uint8() { + let obj_min: EncoderResult = to_bson(&u8::MIN); + assert_matches!(obj_min, Err(EncoderError::UnsupportedUnsignedType)); +} + +#[test] +#[cfg(feature = "u2i")] +fn uint8_u2i() { + let obj: Bson = to_bson(&u8::MIN).unwrap(); + let deser: u8 = from_bson(obj).unwrap(); + assert_eq!(deser, u8::MIN); + + let obj_max: Bson = to_bson(&u8::MAX).unwrap(); + let deser_max: u8 = from_bson(obj_max).unwrap(); + assert_eq!(deser_max, u8::MAX); +} + +#[test] +#[cfg_attr(feature = "u2i", ignore)] +fn uint16() { + let obj_min: EncoderResult = to_bson(&u16::MIN); + assert_matches!(obj_min, Err(EncoderError::UnsupportedUnsignedType)); +} + +#[test] +#[cfg(feature = "u2i")] +fn uint16_u2i() { + let obj: Bson = to_bson(&u16::MIN).unwrap(); + let deser: u16 = from_bson(obj).unwrap(); + assert_eq!(deser, u16::MIN); + + let obj_max: Bson = to_bson(&u16::MAX).unwrap(); + let deser_max: u16 = from_bson(obj_max).unwrap(); + assert_eq!(deser_max, u16::MAX); +} + +#[test] +#[cfg_attr(feature = "u2i", ignore)] +fn uint32() { + let obj_min: EncoderResult = to_bson(&u32::MIN); + assert_matches!(obj_min, Err(EncoderError::UnsupportedUnsignedType)); +} + +#[test] +#[cfg(feature = "u2i")] +fn uint32_u2i() { + let obj_min: Bson = to_bson(&u32::MIN).unwrap(); + let deser_min: u32 = from_bson(obj_min).unwrap(); + assert_eq!(deser_min, u32::MIN); + + let obj_max: Bson = to_bson(&u32::MAX).unwrap(); + let deser_max: u32 = from_bson(obj_max).unwrap(); + assert_eq!(deser_max, u32::MAX); +} + +#[test] +#[cfg_attr(feature = "u2i", ignore)] +fn uint64() { + let obj_min: EncoderResult = to_bson(&u64::MIN); + assert_matches!(obj_min, Err(EncoderError::UnsupportedUnsignedType)); +} + +#[test] +#[cfg(feature = "u2i")] +fn uint64_u2i() { + let obj_min: Bson = to_bson(&u64::MIN).unwrap(); + let deser_min: u64 = from_bson(obj_min).unwrap(); + assert_eq!(deser_min, u64::MIN); + + let obj_max: EncoderResult = to_bson(&u64::MAX); + assert_matches!(obj_max, Err(EncoderError::UnsignedTypesValueExceedsRange(u64::MAX))); +} + #[test] fn int64() { let obj = Bson::I64(101); let i: i64 = from_bson(obj.clone()).unwrap(); - assert_eq!(i, 101); let deser: Bson = to_bson(&i).unwrap();