1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
use byteorder::{ReadBytesExt, WriteBytesExt, NetworkEndian}; use std::error::Error; use std::io::prelude::*; use pg::Pg; use types::{self, IsNull, FromSql, ToSql, ToSqlOutput}; #[cfg(feature = "quickcheck")] mod quickcheck_impls; #[derive(Debug, Clone, PartialEq, Eq)] pub enum PgNumeric { Positive { weight: i16, scale: u16, digits: Vec<i16>, }, Negative { weight: i16, scale: u16, digits: Vec<i16>, }, NaN, } #[derive(Debug, Clone, Copy)] struct InvalidNumericSign(u16); impl ::std::fmt::Display for InvalidNumericSign { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "InvalidNumericSign({0:x})", self.0) } } impl Error for InvalidNumericSign { fn description(&self) -> &str { "sign for numeric field was not one of 0, 0x4000, 0xC000" } } impl FromSql<types::Numeric, Pg> for PgNumeric { fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> { let mut bytes = not_none!(bytes); let ndigits = try!(bytes.read_u16::<NetworkEndian>()); let mut digits = Vec::with_capacity(ndigits as usize); let weight = try!(bytes.read_i16::<NetworkEndian>()); let sign = try!(bytes.read_u16::<NetworkEndian>()); let scale = try!(bytes.read_u16::<NetworkEndian>()); for _ in 0..ndigits { digits.push(try!(bytes.read_i16::<NetworkEndian>())); } match sign { 0 => Ok(PgNumeric::Positive { weight: weight, scale: scale, digits: digits, }), 0x4000 => Ok(PgNumeric::Negative { weight: weight, scale: scale, digits: digits, }), 0xC000 => Ok(PgNumeric::NaN), invalid => Err(Box::new(InvalidNumericSign(invalid))), } } } impl ToSql<types::Numeric, Pg> for PgNumeric { fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Pg>) -> Result<IsNull, Box<Error+Send+Sync>> { let sign = match *self { PgNumeric::Positive { .. } => 0, PgNumeric::Negative { .. } => 0x4000, PgNumeric::NaN => 0xC000, }; let empty_vec = Vec::new(); let digits = match *self { PgNumeric::Positive { ref digits, .. } | PgNumeric::Negative { ref digits, .. } => digits, PgNumeric::NaN => &empty_vec, }; let weight = match *self { PgNumeric::Positive { weight, .. } | PgNumeric::Negative { weight, .. } => weight, PgNumeric::NaN => 0, }; let scale = match *self { PgNumeric::Positive { scale, .. } | PgNumeric::Negative { scale, .. } => scale, PgNumeric::NaN => 0, }; try!(out.write_u16::<NetworkEndian>(digits.len() as u16)); try!(out.write_i16::<NetworkEndian>(weight)); try!(out.write_u16::<NetworkEndian>(sign)); try!(out.write_u16::<NetworkEndian>(scale)); for digit in digits.iter() { try!(out.write_i16::<NetworkEndian>(*digit)); } Ok(IsNull::No) } }