use std::iter::FromIterator;
use std::marker::PhantomData;
use std::fmt::{ self, Write };
use serde::Deserialize;
use bson::{ Bson, Document, from_bson };
use crate::error::{ Error, ErrorKind, Result, ResultExt };
pub struct Cursor<T> {
inner: mongodb::cursor::Cursor,
transform: fn(Document) -> Result<Bson>,
_marker: PhantomData<T>,
}
impl<T> Cursor<T> where T: for<'a> Deserialize<'a> {
#[doc(hidden)]
pub fn from_cursor_and_transform(
inner: mongodb::cursor::Cursor,
transform: fn(Document) -> Result<Bson>,
) -> Self {
Cursor {
inner,
transform,
_marker: PhantomData,
}
}
pub fn next_batch<C: FromIterator<T>>(&mut self) -> Result<C> {
self.inner
.drain_current_batch()
.chain("couldn't retrieve next batch")
.and_then(|docs| self.transform_and_deserialize_many(docs))
}
pub fn next_n<C: FromIterator<T>>(&mut self, n: usize) -> Result<C> {
self.inner
.next_n(n)
.chain("couldn't retrieve documents")
.and_then(|docs| self.transform_and_deserialize_many(docs))
}
pub fn has_next(&mut self) -> Result<bool> {
self.inner.has_next().chain("cursor error")
}
fn transform_and_deserialize_one(&self, mut doc: Document) -> Result<T> {
if let Some(Bson::String(mut errmsg)) = doc.remove("$err") {
if let Ok(code) = doc.get_i32("code") {
write!(errmsg, " (code: {})", code).ok();
} else if let Ok(code) = doc.get_i64("code") {
write!(errmsg, " (code: {})", code).ok();
}
return Err(Error::new(ErrorKind::MongoDbError, errmsg));
}
(self.transform)(doc).and_then(|b| from_bson(b).map_err(From::from))
}
fn transform_and_deserialize_many<C>(&self, docs: Vec<Document>) -> Result<C>
where C: FromIterator<T>
{
docs.into_iter()
.map(|doc| self.transform_and_deserialize_one(doc))
.collect()
}
}
impl<T> Iterator for Cursor<T> where T: for<'a> Deserialize<'a> {
type Item = Result<T>;
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|result| {
result
.chain("can't step Cursor")
.and_then(|doc| self.transform_and_deserialize_one(doc))
})
}
}
impl<T> fmt::Debug for Cursor<T> where T: for<'a> Deserialize<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Cursor").finish()
}
}