Skip to content

Commit 68a8e07

Browse files
lrlnazonyitoo
authored andcommitted
Add Decimal128 support (#118)
* [#53] supports decimal128 and add necessary documents * add documentation for decimal128 methods * add serialize + deserialize for decimal128 * add decimal128 tests + into_i32/_u32 methods
1 parent 8d8e90c commit 68a8e07

File tree

14 files changed

+409
-33
lines changed

14 files changed

+409
-33
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ hostname = "0.1"
3333
hex = "0.3"
3434
md5 = "0.3"
3535
try_from = "0.2"
36+
decimal = "2.0.4"
3637

3738
[dev-dependencies]
3839
assert_matches = "1.2"

src/bson.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use chrono::{DateTime, Timelike, Utc};
2929
use hex;
3030
use serde_json::Value;
3131

32+
use decimal128::Decimal128;
3233
use oid;
3334
use ordered::OrderedDocument;
3435
use spec::{BinarySubtype, ElementType};
@@ -72,6 +73,8 @@ pub enum Bson {
7273
UtcDatetime(DateTime<Utc>),
7374
/// Symbol (Deprecated)
7475
Symbol(String),
76+
/// [128-bit decimal floating point](https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst)
77+
Decimal128(Decimal128),
7578
}
7679

7780
/// Alias for `Vec<Bson>`.
@@ -111,6 +114,7 @@ impl Debug for Bson {
111114
Bson::ObjectId(ref id) => write!(f, "ObjectId({:?})", id),
112115
Bson::UtcDatetime(date_time) => write!(f, "UtcDatetime({:?})", date_time),
113116
Bson::Symbol(ref sym) => write!(f, "Symbol({:?})", sym),
117+
Bson::Decimal128(ref d) => write!(f, "Decimal128({:?})", d),
114118
}
115119
}
116120
}
@@ -152,6 +156,7 @@ impl Display for Bson {
152156
Bson::ObjectId(ref id) => write!(fmt, "ObjectId(\"{}\")", id),
153157
Bson::UtcDatetime(date_time) => write!(fmt, "Date(\"{}\")", date_time),
154158
Bson::Symbol(ref sym) => write!(fmt, "Symbol(\"{}\")", sym),
159+
Bson::Decimal128(ref d) => write!(fmt, "Decimal128({})", d),
155160
}
156161
}
157162
}
@@ -275,9 +280,7 @@ impl From<Value> for Bson {
275280
Value::String(x) => x.into(),
276281
Value::Bool(x) => x.into(),
277282
Value::Array(x) => Bson::Array(x.into_iter().map(Bson::from).collect()),
278-
Value::Object(x) => {
279-
Bson::from_extended_document(x.into_iter().map(|(k, v)| (k, v.into())).collect())
280-
}
283+
Value::Object(x) => Bson::from_extended_document(x.into_iter().map(|(k, v)| (k, v.into())).collect()),
281284
Value::Null => Bson::Null,
282285
}
283286
}
@@ -326,6 +329,7 @@ impl From<Bson> for Value {
326329
}),
327330
// FIXME: Don't know what is the best way to encode Symbol type
328331
Bson::Symbol(v) => json!({ "$symbol": v }),
332+
Bson::Decimal128(ref v) => json!({ "$numberDecimal": v.to_string() }),
329333
}
330334
}
331335
}
@@ -350,6 +354,7 @@ impl Bson {
350354
Bson::ObjectId(..) => ElementType::ObjectId,
351355
Bson::UtcDatetime(..) => ElementType::UtcDatetime,
352356
Bson::Symbol(..) => ElementType::Symbol,
357+
Bson::Decimal128(..) => ElementType::Decimal128Bit,
353358
}
354359
}
355360

@@ -429,6 +434,11 @@ impl Bson {
429434
"$symbol": v.to_owned(),
430435
}
431436
}
437+
Bson::Decimal128(ref v) => {
438+
doc! {
439+
"$numberDecimal" => (v.to_string())
440+
}
441+
}
432442
_ => panic!("Attempted conversion of invalid data type: {}", self),
433443
}
434444
}
@@ -463,6 +473,8 @@ impl Bson {
463473
return Bson::UtcDatetime(Utc.timestamp(long / 1000, ((long % 1000) * 1000000) as u32));
464474
} else if let Ok(sym) = values.get_str("$symbol") {
465475
return Bson::Symbol(sym.to_owned());
476+
} else if let Ok(dec) = values.get_str("$numberDecimal") {
477+
return Bson::Decimal128(dec.parse::<Decimal128>().unwrap());
466478
}
467479
}
468480

src/decimal128.rs

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
//! [BSON Decimal128](https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst) data type representation
2+
3+
use std::fmt;
4+
use std::str::FromStr;
5+
6+
use decimal::d128;
7+
8+
/// Decimal128 type
9+
#[derive(Clone, PartialEq, PartialOrd)]
10+
pub struct Decimal128 {
11+
inner: d128,
12+
}
13+
14+
impl Decimal128 {
15+
/// Construct a `Decimal128` from string.
16+
///
17+
/// For example:
18+
///
19+
/// * `NaN`
20+
/// * `Infinity` or `Inf`
21+
/// * `1.0`, `+37.0`, `0.73e-7`, `.5`
22+
///
23+
/// ```rust
24+
/// use bson::decimal128::Decimal128;
25+
///
26+
/// let dec128 = Decimal128::from_str("1.05E+3");
27+
/// ```
28+
pub fn from_str(s: &str) -> Decimal128 {
29+
Decimal128 { inner: s.parse::<d128>().expect("Invalid Decimal128 string"), }
30+
}
31+
32+
/// Construct a `Decimal128` from a `i32` number.
33+
///
34+
/// ```rust
35+
/// use bson::decimal128::Decimal128;
36+
///
37+
/// let num: i32 = 23;
38+
/// let dec128 = Decimal128::from_i32(num);
39+
/// ```
40+
pub fn from_i32(d: i32) -> Decimal128 {
41+
Decimal128 { inner: From::from(d) }
42+
}
43+
44+
/// Construct a `Decimal128` from a `u32` number.
45+
///
46+
/// ```rust
47+
/// use bson::decimal128::Decimal128;
48+
///
49+
/// let num: u32 = 78;
50+
/// let dec128 = Decimal128::from_u32(num);
51+
/// ```
52+
pub fn from_u32(d: u32) -> Decimal128 {
53+
Decimal128 { inner: From::from(d) }
54+
}
55+
56+
/// Construct a `Decimal128` from a `i32` number.
57+
///
58+
/// ```rust
59+
/// use bson::decimal128::Decimal128;
60+
///
61+
/// let num: i32 = 23;
62+
/// let dec128 = Decimal128::from_i32(num);
63+
/// let int = dec128.into_i32();
64+
/// assert_eq!(int, num);
65+
/// ```
66+
pub fn into_i32(&self) -> i32 {
67+
Into::into(self.inner)
68+
}
69+
70+
/// Construct a `Decimal128` from a `i32` number.
71+
///
72+
/// ```rust
73+
/// use bson::decimal128::Decimal128;
74+
///
75+
/// let num: u32 = 23;
76+
/// let dec128 = Decimal128::from_u32(num);
77+
/// let int = dec128.into_u32();
78+
/// assert_eq!(int, num);
79+
/// ```
80+
pub fn into_u32(&self) -> u32 {
81+
Into::into(self.inner)
82+
}
83+
84+
/// Create a new Decimal128 as `0`.
85+
///
86+
/// ```rust
87+
/// use bson::decimal128::Decimal128;
88+
///
89+
/// let dec128 = Decimal128::zero();
90+
/// ```
91+
pub fn zero() -> Decimal128 {
92+
Decimal128 { inner: d128::zero() }
93+
}
94+
95+
#[doc(hidden)]
96+
pub unsafe fn from_raw_bytes_le(mut raw: [u8; 16]) -> Decimal128 {
97+
if cfg!(target_endian = "big") {
98+
raw.reverse();
99+
}
100+
101+
Decimal128 { inner: d128::from_raw_bytes(raw), }
102+
}
103+
104+
#[doc(hidden)]
105+
pub fn to_raw_bytes_le(&self) -> [u8; 16] {
106+
let mut buf = self.inner.to_raw_bytes();
107+
if cfg!(target_endian = "big") {
108+
buf.reverse();
109+
}
110+
buf
111+
}
112+
113+
/// Check if value is `NaN`
114+
///
115+
/// ```rust
116+
/// use bson::decimal128::Decimal128;
117+
///
118+
/// let num: u32 = 78;
119+
/// let dec128 = Decimal128::from_u32(num);
120+
/// assert!(!dec128.is_nan());
121+
/// ```
122+
pub fn is_nan(&self) -> bool {
123+
self.inner.is_nan()
124+
}
125+
126+
/// Check if value is 0
127+
///
128+
/// ```rust
129+
/// use bson::decimal128::Decimal128;
130+
///
131+
/// let num: u32 = 0;
132+
/// let dec128 = Decimal128::from_u32(num);
133+
/// assert!(dec128.is_zero());
134+
/// ```
135+
pub fn is_zero(&self) -> bool {
136+
self.inner.is_zero()
137+
}
138+
}
139+
140+
impl fmt::Debug for Decimal128 {
141+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142+
write!(f, "Decimal(\"{:?}\")", self.inner)
143+
}
144+
}
145+
146+
impl fmt::Display for Decimal128 {
147+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148+
write!(f, "{}", self.inner)
149+
}
150+
}
151+
152+
impl fmt::LowerHex for Decimal128 {
153+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154+
<d128 as fmt::LowerHex>::fmt(&self.inner, f)
155+
}
156+
}
157+
158+
impl fmt::LowerExp for Decimal128 {
159+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160+
<d128 as fmt::LowerExp>::fmt(&self.inner, f)
161+
}
162+
}
163+
164+
impl FromStr for Decimal128 {
165+
type Err = ();
166+
fn from_str(s: &str) -> Result<Decimal128, ()> {
167+
Ok(Decimal128::from_str(s))
168+
}
169+
}
170+
171+
impl Into<d128> for Decimal128 {
172+
fn into(self) -> d128 {
173+
self.inner
174+
}
175+
}
176+
177+
impl From<d128> for Decimal128 {
178+
fn from(d: d128) -> Decimal128 {
179+
Decimal128 { inner: d }
180+
}
181+
}
182+
183+
impl Default for Decimal128 {
184+
fn default() -> Decimal128 {
185+
Decimal128::zero()
186+
}
187+
}
188+
189+
#[cfg(test)]
190+
mod test {
191+
use super::*;
192+
193+
#[test]
194+
fn decimal128_string() {
195+
assert!(Decimal128::from_str("0").is_zero());
196+
assert!(!Decimal128::from_str("12").is_nan());
197+
assert!(!Decimal128::from_str("-76").is_nan());
198+
assert!(!Decimal128::from_str("12.70").is_nan());
199+
assert!(!Decimal128::from_str("+0.003").is_nan());
200+
assert!(!Decimal128::from_str("017.").is_nan());
201+
assert!(!Decimal128::from_str(".5").is_nan());
202+
assert!(!Decimal128::from_str("4E+9").is_nan());
203+
assert!(!Decimal128::from_str("0.73e-7").is_nan());
204+
assert!(!Decimal128::from_str("Inf").is_nan());
205+
assert!(!Decimal128::from_str("-infinity").is_nan());
206+
assert!(Decimal128::from_str("NaN").is_nan());
207+
}
208+
209+
#[test]
210+
fn decimal128_i32() {
211+
let num: i32 = 89;
212+
let dec128 = Decimal128::from_i32(num);
213+
214+
assert!(!dec128.is_nan());
215+
assert!(!dec128.is_zero());
216+
assert_eq!(dec128.into_i32(), num);
217+
}
218+
219+
#[test]
220+
fn decimal128_u32() {
221+
let num: u32 = 89;
222+
let dec128 = Decimal128::from_u32(num);
223+
224+
assert!(!dec128.is_nan());
225+
assert!(!dec128.is_zero());
226+
assert_eq!(dec128.into_u32(), num);
227+
}
228+
229+
#[test]
230+
fn decimal128_0() {
231+
let dec128 = Decimal128::zero();
232+
assert!(dec128.is_zero());
233+
}
234+
235+
#[test]
236+
fn decimal128_is_zero() {
237+
let dec128 = Decimal128::from_i32(234);
238+
assert!(!dec128.is_zero());
239+
240+
let dec128_0 = Decimal128::from_i32(0);
241+
assert!(dec128_0.is_zero());
242+
}
243+
244+
#[test]
245+
fn decimal128_is_nan() {
246+
let dec128 = Decimal128::from_str("NaN");
247+
assert!(dec128.is_nan());
248+
249+
let dec128 = Decimal128::from_i32(234);
250+
assert!(!dec128.is_nan());
251+
}
252+
}

src/decoder/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ pub use self::error::{DecoderError, DecoderResult};
2828
pub use self::serde::Decoder;
2929

3030
use std::io::Read;
31+
use std::mem;
3132

3233
use byteorder::{LittleEndian, ReadBytesExt};
3334
use chrono::offset::{LocalResult, TimeZone};
3435
use chrono::Utc;
36+
use decimal128::Decimal128;
3537

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

92+
#[inline]
93+
fn read_f128<R: Read + ?Sized>(reader: &mut R) -> DecoderResult<Decimal128> {
94+
let mut local_buf: [u8; 16] = unsafe { mem::uninitialized() };
95+
try!(reader.read_exact(&mut local_buf));
96+
let val = unsafe { Decimal128::from_raw_bytes_le(local_buf) };
97+
Ok(val)
98+
}
99+
90100
/// Attempt to decode a `Document` from a byte stream.
91101
pub fn decode_document<R: Read + ?Sized>(reader: &mut R) -> DecoderResult<Document> {
92102
let mut doc = Document::new();
@@ -222,6 +232,7 @@ fn decode_bson<R: Read + ?Sized>(reader: &mut R, tag: u8, utf8_lossy: bool) -> D
222232
}
223233
}
224234
Some(Symbol) => read_string(reader, utf8_lossy).map(Bson::Symbol),
235+
Some(Decimal128Bit) => read_f128(reader).map(Bson::Decimal128),
225236
Some(Undefined) | Some(DbPointer) | Some(MaxKey) | Some(MinKey) | None => {
226237
Err(DecoderError::UnrecognizedElementType(tag))
227238
}

0 commit comments

Comments
 (0)