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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
extern crate byteorder; use self::byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; use super::option::UnexpectedNullError; use types::{FromSql, ToSql, IsNull}; use types; use std::error::Error; use std::io::Write; #[cfg(feature = "quickcheck")] mod quickcheck_impls; impl FromSql<types::Float> for f32 { fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error>> { let mut bytes = not_none!(bytes); bytes.read_f32::<BigEndian>().map_err(|e| Box::new(e) as Box<Error>) } } impl ToSql<types::Float> for f32 { fn to_sql<W: Write>(&self, out: &mut W) -> Result<IsNull, Box<Error>> { out.write_f32::<BigEndian>(*self) .map(|_| IsNull::No) .map_err(|e| Box::new(e) as Box<Error>) } } impl FromSql<types::Double> for f64 { fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error>> { let mut bytes = not_none!(bytes); bytes.read_f64::<BigEndian>().map_err(|e| Box::new(e) as Box<Error>) } } impl ToSql<types::Double> for f64 { fn to_sql<W: Write>(&self, out: &mut W) -> Result<IsNull, Box<Error>> { out.write_f64::<BigEndian>(*self) .map(|_| IsNull::No) .map_err(|e| Box::new(e) as Box<Error>) } } #[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> for PgNumeric { fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error>> { let mut bytes = not_none!(bytes); let ndigits = try!(bytes.read_u16::<BigEndian>()); let mut digits = Vec::with_capacity(ndigits as usize); let weight = try!(bytes.read_i16::<BigEndian>()); let sign = try!(bytes.read_u16::<BigEndian>()); let scale = try!(bytes.read_u16::<BigEndian>()); for _ in 0..ndigits { digits.push(try!(bytes.read_i16::<BigEndian>())); } 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> for PgNumeric { fn to_sql<W: Write>(&self, out: &mut W) -> Result<IsNull, Box<Error>> { 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, .. } => digits, &PgNumeric::Negative { ref digits, .. } => digits, &PgNumeric::NaN => &empty_vec, }; let weight = match self { &PgNumeric::Positive { weight, .. } => weight, &PgNumeric::Negative { weight, .. } => weight, &PgNumeric::NaN => 0, }; let scale = match self { &PgNumeric::Positive { scale, .. } => scale, &PgNumeric::Negative { scale, .. } => scale, &PgNumeric::NaN => 0, }; try!(out.write_u16::<BigEndian>(digits.len() as u16)); try!(out.write_i16::<BigEndian>(weight)); try!(out.write_u16::<BigEndian>(sign)); try!(out.write_u16::<BigEndian>(scale)); for digit in digits.iter() { try!(out.write_i16::<BigEndian>(*digit)); } Ok(IsNull::No) } }