diesel/pg/types/floats/
mod.rs

1use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
2use std::error::Error;
3use std::io::prelude::*;
4
5use deserialize::{self, FromSql};
6use pg::Pg;
7use serialize::{self, IsNull, Output, ToSql};
8use sql_types;
9
10#[cfg(feature = "quickcheck")]
11mod quickcheck_impls;
12
13#[derive(Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression)]
14#[sql_type = "sql_types::Numeric"]
15/// Represents a NUMERIC value, closely mirroring the PG wire protocol
16/// representation
17pub enum PgNumeric {
18    /// A positive number
19    Positive {
20        /// How many digits come before the decimal point?
21        weight: i16,
22        /// How many significant digits are there?
23        scale: u16,
24        /// The digits in this number, stored in base 10000
25        digits: Vec<i16>,
26    },
27    /// A negative number
28    Negative {
29        /// How many digits come before the decimal point?
30        weight: i16,
31        /// How many significant digits are there?
32        scale: u16,
33        /// The digits in this number, stored in base 10000
34        digits: Vec<i16>,
35    },
36    /// Not a number
37    NaN,
38}
39
40#[derive(Debug, Clone, Copy)]
41struct InvalidNumericSign(u16);
42
43impl ::std::fmt::Display for InvalidNumericSign {
44    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
45        write!(f, "InvalidNumericSign({0:x})", self.0)
46    }
47}
48
49impl Error for InvalidNumericSign {
50    fn description(&self) -> &str {
51        "sign for numeric field was not one of 0, 0x4000, 0xC000"
52    }
53}
54
55impl FromSql<sql_types::Numeric, Pg> for PgNumeric {
56    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
57        let mut bytes = not_none!(bytes);
58        let digit_count = bytes.read_u16::<NetworkEndian>()?;
59        let mut digits = Vec::with_capacity(digit_count as usize);
60        let weight = bytes.read_i16::<NetworkEndian>()?;
61        let sign = bytes.read_u16::<NetworkEndian>()?;
62        let scale = bytes.read_u16::<NetworkEndian>()?;
63        for _ in 0..digit_count {
64            digits.push(bytes.read_i16::<NetworkEndian>()?);
65        }
66
67        match sign {
68            0 => Ok(PgNumeric::Positive {
69                weight: weight,
70                scale: scale,
71                digits: digits,
72            }),
73            0x4000 => Ok(PgNumeric::Negative {
74                weight: weight,
75                scale: scale,
76                digits: digits,
77            }),
78            0xC000 => Ok(PgNumeric::NaN),
79            invalid => Err(Box::new(InvalidNumericSign(invalid))),
80        }
81    }
82}
83
84impl ToSql<sql_types::Numeric, Pg> for PgNumeric {
85    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
86        let sign = match *self {
87            PgNumeric::Positive { .. } => 0,
88            PgNumeric::Negative { .. } => 0x4000,
89            PgNumeric::NaN => 0xC000,
90        };
91        let empty_vec = Vec::new();
92        let digits = match *self {
93            PgNumeric::Positive { ref digits, .. } | PgNumeric::Negative { ref digits, .. } => {
94                digits
95            }
96            PgNumeric::NaN => &empty_vec,
97        };
98        let weight = match *self {
99            PgNumeric::Positive { weight, .. } | PgNumeric::Negative { weight, .. } => weight,
100            PgNumeric::NaN => 0,
101        };
102        let scale = match *self {
103            PgNumeric::Positive { scale, .. } | PgNumeric::Negative { scale, .. } => scale,
104            PgNumeric::NaN => 0,
105        };
106        out.write_u16::<NetworkEndian>(digits.len() as u16)?;
107        out.write_i16::<NetworkEndian>(weight)?;
108        out.write_u16::<NetworkEndian>(sign)?;
109        out.write_u16::<NetworkEndian>(scale)?;
110        for digit in digits.iter() {
111            out.write_i16::<NetworkEndian>(*digit)?;
112        }
113
114        Ok(IsNull::No)
115    }
116}