Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ hostname = "0.1"
hex = "0.3"
md5 = "0.3"
try_from = "0.2"
decimal = "2.0.4"

[dev-dependencies]
assert_matches = "1.2"
Expand Down
18 changes: 15 additions & 3 deletions src/bson.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use chrono::{DateTime, Timelike, Utc};
use hex;
use serde_json::Value;

use decimal128::Decimal128;
use oid;
use ordered::OrderedDocument;
use spec::{BinarySubtype, ElementType};
Expand Down Expand Up @@ -72,6 +73,8 @@ pub enum Bson {
UtcDatetime(DateTime<Utc>),
/// Symbol (Deprecated)
Symbol(String),
/// [128-bit decimal floating point](https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst)
Decimal128(Decimal128),
}

/// Alias for `Vec<Bson>`.
Expand Down Expand Up @@ -111,6 +114,7 @@ impl Debug for Bson {
Bson::ObjectId(ref id) => write!(f, "ObjectId({:?})", id),
Bson::UtcDatetime(date_time) => write!(f, "UtcDatetime({:?})", date_time),
Bson::Symbol(ref sym) => write!(f, "Symbol({:?})", sym),
Bson::Decimal128(ref d) => write!(f, "Decimal128({:?})", d),
}
}
}
Expand Down Expand Up @@ -152,6 +156,7 @@ impl Display for Bson {
Bson::ObjectId(ref id) => write!(fmt, "ObjectId(\"{}\")", id),
Bson::UtcDatetime(date_time) => write!(fmt, "Date(\"{}\")", date_time),
Bson::Symbol(ref sym) => write!(fmt, "Symbol(\"{}\")", sym),
Bson::Decimal128(ref d) => write!(fmt, "Decimal128({})", d),
}
}
}
Expand Down Expand Up @@ -275,9 +280,7 @@ impl From<Value> for Bson {
Value::String(x) => x.into(),
Value::Bool(x) => x.into(),
Value::Array(x) => Bson::Array(x.into_iter().map(Bson::from).collect()),
Value::Object(x) => {
Bson::from_extended_document(x.into_iter().map(|(k, v)| (k, v.into())).collect())
}
Value::Object(x) => Bson::from_extended_document(x.into_iter().map(|(k, v)| (k, v.into())).collect()),
Value::Null => Bson::Null,
}
}
Expand Down Expand Up @@ -326,6 +329,7 @@ impl From<Bson> for Value {
}),
// FIXME: Don't know what is the best way to encode Symbol type
Bson::Symbol(v) => json!({ "$symbol": v }),
Bson::Decimal128(ref v) => json!({ "$numberDecimal": v.to_string() }),
}
}
}
Expand All @@ -350,6 +354,7 @@ impl Bson {
Bson::ObjectId(..) => ElementType::ObjectId,
Bson::UtcDatetime(..) => ElementType::UtcDatetime,
Bson::Symbol(..) => ElementType::Symbol,
Bson::Decimal128(..) => ElementType::Decimal128Bit,
}
}

Expand Down Expand Up @@ -429,6 +434,11 @@ impl Bson {
"$symbol": v.to_owned(),
}
}
Bson::Decimal128(ref v) => {
doc! {
"$numberDecimal" => (v.to_string())
}
}
_ => panic!("Attempted conversion of invalid data type: {}", self),
}
}
Expand Down Expand Up @@ -463,6 +473,8 @@ impl Bson {
return Bson::UtcDatetime(Utc.timestamp(long / 1000, ((long % 1000) * 1000000) as u32));
} else if let Ok(sym) = values.get_str("$symbol") {
return Bson::Symbol(sym.to_owned());
} else if let Ok(dec) = values.get_str("$numberDecimal") {
return Bson::Decimal128(dec.parse::<Decimal128>().unwrap());
}
}

Expand Down
252 changes: 252 additions & 0 deletions src/decimal128.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
//! [BSON Decimal128](https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst) data type representation

use std::fmt;
use std::str::FromStr;

use decimal::d128;

/// Decimal128 type
#[derive(Clone, PartialEq, PartialOrd)]
pub struct Decimal128 {
inner: d128,
}

impl Decimal128 {
/// Construct a `Decimal128` from string.
///
/// For example:
///
/// * `NaN`
/// * `Infinity` or `Inf`
/// * `1.0`, `+37.0`, `0.73e-7`, `.5`
///
/// ```rust
/// use bson::decimal128::Decimal128;
///
/// let dec128 = Decimal128::from_str("1.05E+3");
/// ```
pub fn from_str(s: &str) -> Decimal128 {
Decimal128 { inner: s.parse::<d128>().expect("Invalid Decimal128 string"), }
}

/// Construct a `Decimal128` from a `i32` number.
///
/// ```rust
/// use bson::decimal128::Decimal128;
///
/// let num: i32 = 23;
/// let dec128 = Decimal128::from_i32(num);
/// ```
pub fn from_i32(d: i32) -> Decimal128 {
Decimal128 { inner: From::from(d) }
}

/// Construct a `Decimal128` from a `u32` number.
///
/// ```rust
/// use bson::decimal128::Decimal128;
///
/// let num: u32 = 78;
/// let dec128 = Decimal128::from_u32(num);
/// ```
pub fn from_u32(d: u32) -> Decimal128 {
Decimal128 { inner: From::from(d) }
}

/// Construct a `Decimal128` from a `i32` number.
///
/// ```rust
/// use bson::decimal128::Decimal128;
///
/// let num: i32 = 23;
/// let dec128 = Decimal128::from_i32(num);
/// let int = dec128.into_i32();
/// assert_eq!(int, num);
/// ```
pub fn into_i32(&self) -> i32 {
Into::into(self.inner)
}

/// Construct a `Decimal128` from a `i32` number.
///
/// ```rust
/// use bson::decimal128::Decimal128;
///
/// let num: u32 = 23;
/// let dec128 = Decimal128::from_u32(num);
/// let int = dec128.into_u32();
/// assert_eq!(int, num);
/// ```
pub fn into_u32(&self) -> u32 {
Into::into(self.inner)
}

/// Create a new Decimal128 as `0`.
///
/// ```rust
/// use bson::decimal128::Decimal128;
///
/// let dec128 = Decimal128::zero();
/// ```
pub fn zero() -> Decimal128 {
Decimal128 { inner: d128::zero() }
}

#[doc(hidden)]
pub unsafe fn from_raw_bytes_le(mut raw: [u8; 16]) -> Decimal128 {
if cfg!(target_endian = "big") {
raw.reverse();
}

Decimal128 { inner: d128::from_raw_bytes(raw), }
}

#[doc(hidden)]
pub fn to_raw_bytes_le(&self) -> [u8; 16] {
let mut buf = self.inner.to_raw_bytes();
if cfg!(target_endian = "big") {
buf.reverse();
}
buf
}

/// Check if value is `NaN`
///
/// ```rust
/// use bson::decimal128::Decimal128;
///
/// let num: u32 = 78;
/// let dec128 = Decimal128::from_u32(num);
/// assert!(!dec128.is_nan());
/// ```
pub fn is_nan(&self) -> bool {
self.inner.is_nan()
}

/// Check if value is 0
///
/// ```rust
/// use bson::decimal128::Decimal128;
///
/// let num: u32 = 0;
/// let dec128 = Decimal128::from_u32(num);
/// assert!(dec128.is_zero());
/// ```
pub fn is_zero(&self) -> bool {
self.inner.is_zero()
}
}

impl fmt::Debug for Decimal128 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Decimal(\"{:?}\")", self.inner)
}
}

impl fmt::Display for Decimal128 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.inner)
}
}

impl fmt::LowerHex for Decimal128 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<d128 as fmt::LowerHex>::fmt(&self.inner, f)
}
}

impl fmt::LowerExp for Decimal128 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<d128 as fmt::LowerExp>::fmt(&self.inner, f)
}
}

impl FromStr for Decimal128 {
type Err = ();
fn from_str(s: &str) -> Result<Decimal128, ()> {
Ok(Decimal128::from_str(s))
}
}

impl Into<d128> for Decimal128 {
fn into(self) -> d128 {
self.inner
}
}

impl From<d128> for Decimal128 {
fn from(d: d128) -> Decimal128 {
Decimal128 { inner: d }
}
}

impl Default for Decimal128 {
fn default() -> Decimal128 {
Decimal128::zero()
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn decimal128_string() {
assert!(Decimal128::from_str("0").is_zero());
assert!(!Decimal128::from_str("12").is_nan());
assert!(!Decimal128::from_str("-76").is_nan());
assert!(!Decimal128::from_str("12.70").is_nan());
assert!(!Decimal128::from_str("+0.003").is_nan());
assert!(!Decimal128::from_str("017.").is_nan());
assert!(!Decimal128::from_str(".5").is_nan());
assert!(!Decimal128::from_str("4E+9").is_nan());
assert!(!Decimal128::from_str("0.73e-7").is_nan());
assert!(!Decimal128::from_str("Inf").is_nan());
assert!(!Decimal128::from_str("-infinity").is_nan());
assert!(Decimal128::from_str("NaN").is_nan());
}

#[test]
fn decimal128_i32() {
let num: i32 = 89;
let dec128 = Decimal128::from_i32(num);

assert!(!dec128.is_nan());
assert!(!dec128.is_zero());
assert_eq!(dec128.into_i32(), num);
}

#[test]
fn decimal128_u32() {
let num: u32 = 89;
let dec128 = Decimal128::from_u32(num);

assert!(!dec128.is_nan());
assert!(!dec128.is_zero());
assert_eq!(dec128.into_u32(), num);
}

#[test]
fn decimal128_0() {
let dec128 = Decimal128::zero();
assert!(dec128.is_zero());
}

#[test]
fn decimal128_is_zero() {
let dec128 = Decimal128::from_i32(234);
assert!(!dec128.is_zero());

let dec128_0 = Decimal128::from_i32(0);
assert!(dec128_0.is_zero());
}

#[test]
fn decimal128_is_nan() {
let dec128 = Decimal128::from_str("NaN");
assert!(dec128.is_nan());

let dec128 = Decimal128::from_i32(234);
assert!(!dec128.is_nan());
}
}
11 changes: 11 additions & 0 deletions src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ pub use self::error::{DecoderError, DecoderResult};
pub use self::serde::Decoder;

use std::io::Read;
use std::mem;

use byteorder::{LittleEndian, ReadBytesExt};
use chrono::offset::{LocalResult, TimeZone};
use chrono::Utc;
use decimal128::Decimal128;

use bson::{Array, Bson, Document};
use oid;
Expand Down Expand Up @@ -87,6 +89,14 @@ fn read_i64<R: Read + ?Sized>(reader: &mut R) -> DecoderResult<i64> {
reader.read_i64::<LittleEndian>().map_err(From::from)
}

#[inline]
fn read_f128<R: Read + ?Sized>(reader: &mut R) -> DecoderResult<Decimal128> {
let mut local_buf: [u8; 16] = unsafe { mem::uninitialized() };
try!(reader.read_exact(&mut local_buf));
let val = unsafe { Decimal128::from_raw_bytes_le(local_buf) };
Ok(val)
}

/// Attempt to decode a `Document` from a byte stream.
pub fn decode_document<R: Read + ?Sized>(reader: &mut R) -> DecoderResult<Document> {
let mut doc = Document::new();
Expand Down Expand Up @@ -222,6 +232,7 @@ fn decode_bson<R: Read + ?Sized>(reader: &mut R, tag: u8, utf8_lossy: bool) -> D
}
}
Some(Symbol) => read_string(reader, utf8_lossy).map(Bson::Symbol),
Some(Decimal128Bit) => read_f128(reader).map(Bson::Decimal128),
Some(Undefined) | Some(DbPointer) | Some(MaxKey) | Some(MinKey) | None => {
Err(DecoderError::UnrecognizedElementType(tag))
}
Expand Down
Loading