use std::io::{self, Write};
use std::iter::IntoIterator;
use std::{mem, error, fmt};
use chrono::Timelike;
use byteorder::{self, LittleEndian, WriteBytesExt};
use bson::Bson;
#[derive(Debug)]
pub enum EncoderError {
IoError(io::Error),
}
impl From<io::Error> for EncoderError {
fn from(err: io::Error) -> EncoderError {
EncoderError::IoError(err)
}
}
impl From<byteorder::Error> for EncoderError {
fn from(err: byteorder::Error) -> EncoderError {
EncoderError::IoError(From::from(err))
}
}
impl fmt::Display for EncoderError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
&EncoderError::IoError(ref inner) => inner.fmt(fmt)
}
}
}
impl error::Error for EncoderError {
fn description(&self) -> &str {
match self {
&EncoderError::IoError(ref inner) => inner.description(),
}
}
fn cause(&self) -> Option<&error::Error> {
match self {
&EncoderError::IoError(ref inner) => Some(inner)
}
}
}
pub type EncoderResult<T> = Result<T, EncoderError>;
fn write_string<W: Write + ?Sized>(writer: &mut W, s: &str) -> EncoderResult<()> {
try!(writer.write_i32::<LittleEndian>(s.len() as i32 + 1));
try!(writer.write_all(s.as_bytes()));
try!(writer.write_u8(0));
Ok(())
}
fn write_cstring<W: Write + ?Sized>(writer: &mut W, s: &str) -> EncoderResult<()> {
try!(writer.write_all(s.as_bytes()));
try!(writer.write_u8(0));
Ok(())
}
#[inline]
fn write_i32<W: Write + ?Sized>(writer: &mut W, val: i32) -> EncoderResult<()> {
writer.write_i32::<LittleEndian>(val).map_err(From::from)
}
#[inline]
fn write_i64<W: Write + ?Sized>(writer: &mut W, val: i64) -> EncoderResult<()> {
writer.write_i64::<LittleEndian>(val).map_err(From::from)
}
#[inline]
fn write_f64<W: Write + ?Sized>(writer: &mut W, val: f64) -> EncoderResult<()> {
writer.write_f64::<LittleEndian>(val).map_err(From::from)
}
fn encode_array<W: Write + ?Sized>(writer: &mut W, arr: &[Bson]) -> EncoderResult<()> {
let mut buf = Vec::new();
for (key, val) in arr.iter().enumerate() {
try!(encode_bson(&mut buf, &key.to_string(), val));
}
try!(write_i32(writer, (buf.len() + mem::size_of::<i32>() + mem::size_of::<u8>()) as i32));
try!(writer.write_all(&buf));
try!(writer.write_u8(0));
Ok(())
}
pub fn encode_document
<'a, S: AsRef<str> + 'a, W: Write + ?Sized, D: IntoIterator<Item=(&'a S, &'a Bson)>>
(writer: &mut W, doc: D) -> EncoderResult<()>
{
let mut buf = Vec::new();
for (key, val) in doc.into_iter() {
try!(encode_bson(&mut buf, key.as_ref(), val));
}
try!(write_i32(writer, (buf.len() + mem::size_of::<i32>() + mem::size_of::<u8>()) as i32));
try!(writer.write_all(&buf));
try!(writer.write_u8(0));
Ok(())
}
fn encode_bson<W: Write + ?Sized>(writer: &mut W, key: &str, val: &Bson) -> EncoderResult<()> {
try!(writer.write_u8(val.element_type() as u8));
try!(write_cstring(writer, key));
match val {
&Bson::FloatingPoint(v) => write_f64(writer, v),
&Bson::String(ref v) => write_string(writer, &v),
&Bson::Array(ref v) => encode_array(writer, &v),
&Bson::Document(ref v) => encode_document(writer, v),
&Bson::Boolean(v) => writer.write_u8(if v { 0x01 } else { 0x00 }).map_err(From::from),
&Bson::RegExp(ref pat, ref opt) => {
try!(write_cstring(writer, pat));
write_cstring(writer, opt)
},
&Bson::JavaScriptCode(ref code) => write_string(writer, &code),
&Bson::ObjectId(ref id) => writer.write_all(&id.bytes()).map_err(From::from),
&Bson::JavaScriptCodeWithScope(ref code, ref scope) => {
let mut buf = Vec::new();
try!(write_string(&mut buf, code));
try!(encode_document(&mut buf, scope));
try!(write_i32(writer, buf.len() as i32 + 1));
writer.write_all(&buf).map_err(From::from)
},
&Bson::I32(v) => write_i32(writer, v),
&Bson::I64(v) => write_i64(writer, v),
&Bson::TimeStamp(v) => write_i64(writer, v),
&Bson::Binary(subtype, ref data) => {
try!(write_i32(writer, data.len() as i32));
try!(writer.write_u8(From::from(subtype)));
writer.write_all(data).map_err(From::from)
},
&Bson::UtcDatetime(ref v) => {
write_i64(writer, (v.timestamp() * 1000) + (v.nanosecond() / 1000000) as i64)
},
&Bson::Null => Ok(())
}
}