use std::ptr;
use std::ffi::CStr;
use std::borrow::Cow;
use std::time::Duration;
use crate::mongoc::bindings;
use crate::bsonc;
use bson::Document;
use super::{Result,BulkOperationResult,BulkOperationError};
use super::CommandAndFindOptions;
use super::{BsoncError,InvalidParamsError};
use super::bsonc::Bsonc;
use super::client::Client;
use super::cursor;
use super::cursor::{Cursor,TailingCursor};
use super::database::Database;
use super::flags::{Flags,FlagsValue,InsertFlag,QueryFlag,RemoveFlag,UpdateFlag};
use super::write_concern::WriteConcern;
use super::read_prefs::ReadPrefs;
#[doc(hidden)]
pub enum CreatedBy<'a> {
BorrowedClient(&'a Client<'a>),
OwnedClient(Client<'a>),
BorrowedDatabase(&'a Database<'a>),
OwnedDatabase(Database<'a>)
}
pub struct Collection<'a> {
_created_by: CreatedBy<'a>,
inner: *mut bindings::mongoc_collection_t
}
pub struct AggregateOptions {
pub query_flags: Flags<QueryFlag>,
pub options: Option<Document>,
pub read_prefs: Option<ReadPrefs>
}
impl AggregateOptions {
pub fn default() -> AggregateOptions {
AggregateOptions {
query_flags: Flags::new(),
options: None,
read_prefs: None
}
}
}
pub struct BulkOperationOptions {
pub ordered: bool,
pub write_concern: WriteConcern
}
impl BulkOperationOptions {
pub fn default() -> BulkOperationOptions {
BulkOperationOptions {
ordered: false,
write_concern: WriteConcern::default()
}
}
}
pub struct FindAndModifyOptions {
pub sort: Option<Document>,
pub new: bool,
pub fields: Option<Document>
}
impl FindAndModifyOptions {
pub fn default() -> FindAndModifyOptions {
FindAndModifyOptions {
sort: None,
new: false,
fields: None
}
}
fn fields_bsonc(&self) -> Option<bsonc::Bsonc> {
match self.fields {
Some(ref f) => Some(bsonc::Bsonc::from_document(f).unwrap()),
None => None
}
}
}
pub enum FindAndModifyOperation<'a> {
Update(&'a Document),
Upsert(&'a Document),
Remove
}
pub struct CountOptions {
pub query_flags: Flags<QueryFlag>,
pub skip: u32,
pub limit: u32,
pub opts: Option<Document>,
pub read_prefs: Option<ReadPrefs>
}
impl CountOptions {
pub fn default() -> CountOptions {
CountOptions {
query_flags: Flags::new(),
skip: 0,
limit: 0,
opts: None,
read_prefs: None
}
}
}
pub struct InsertOptions {
pub insert_flags: Flags<InsertFlag>,
pub write_concern: WriteConcern
}
impl InsertOptions {
pub fn default() -> InsertOptions {
InsertOptions {
insert_flags: Flags::new(),
write_concern: WriteConcern::default()
}
}
}
pub struct RemoveOptions {
pub remove_flags: Flags<RemoveFlag>,
pub write_concern: WriteConcern
}
impl RemoveOptions {
pub fn default() -> RemoveOptions {
RemoveOptions {
remove_flags: Flags::new(),
write_concern: WriteConcern::default()
}
}
}
pub struct UpdateOptions {
pub update_flags: Flags<UpdateFlag>,
pub write_concern: WriteConcern
}
impl UpdateOptions {
pub fn default() -> UpdateOptions {
UpdateOptions {
update_flags: Flags::new(),
write_concern: WriteConcern::default()
}
}
}
pub struct TailOptions {
pub wait_duration: Duration,
pub max_retries: u32
}
impl TailOptions {
pub fn default() -> TailOptions {
TailOptions {
wait_duration: Duration::from_millis(500),
max_retries: 5
}
}
}
impl<'a> Collection<'a> {
#[doc(hidden)]
pub fn new(
created_by: CreatedBy<'a>,
inner: *mut bindings::mongoc_collection_t
) -> Collection<'a> {
assert!(!inner.is_null());
Collection {
_created_by: created_by,
inner: inner
}
}
pub fn aggregate(
&'a self,
pipeline: &Document,
options: Option<&AggregateOptions>
) -> Result<Cursor<'a>> {
let default_options = AggregateOptions::default();
let options = options.unwrap_or(&default_options);
let cursor_ptr = unsafe {
bindings::mongoc_collection_aggregate(
self.inner,
options.query_flags.flags(),
Bsonc::from_document(pipeline)?.inner(),
match options.options {
Some(ref o) => {
Bsonc::from_document(o)?.inner()
},
None => ptr::null()
},
match options.read_prefs {
Some(ref prefs) => prefs.inner(),
None => ptr::null()
}
)
};
if cursor_ptr.is_null() {
return Err(InvalidParamsError.into())
}
Ok(Cursor::new(
cursor::CreatedBy::Collection(self),
cursor_ptr,
None
))
}
pub fn command(
&'a self,
command: Document,
options: Option<&CommandAndFindOptions>
) -> Result<Cursor<'a>> {
assert!(!self.inner.is_null());
let default_options = CommandAndFindOptions::default();
let options = options.unwrap_or(&default_options);
let fields_bsonc = options.fields_bsonc();
let cursor_ptr = unsafe {
bindings::mongoc_collection_command(
self.inner,
options.query_flags.flags(),
options.skip,
options.limit,
options.batch_size,
Bsonc::from_document(&command)?.inner(),
match fields_bsonc {
Some(ref f) => f.inner(),
None => ptr::null()
},
match options.read_prefs {
Some(ref prefs) => prefs.inner(),
None => ptr::null()
}
)
};
if cursor_ptr.is_null() {
return Err(InvalidParamsError.into())
}
Ok(Cursor::new(
cursor::CreatedBy::Collection(self),
cursor_ptr,
fields_bsonc
))
}
pub fn command_simple(
&'a self,
command: Document,
read_prefs: Option<&ReadPrefs>
) -> Result<Document> {
assert!(!self.inner.is_null());
let mut reply = Bsonc::new();
let mut error = BsoncError::empty();
let success = unsafe {
bindings::mongoc_collection_command_simple(
self.inner,
Bsonc::from_document(&command)?.inner(),
match read_prefs {
Some(ref prefs) => prefs.inner(),
None => ptr::null()
},
reply.mut_inner(),
error.mut_inner()
)
};
if success == 1 {
match reply.as_document() {
Ok(document) => return Ok(document),
Err(error) => return Err(error.into())
}
} else {
Err(error.into())
}
}
pub fn count(
&self,
query: &Document,
options: Option<&CountOptions>
) -> Result<i64> {
assert!(!self.inner.is_null());
let default_options = CountOptions::default();
let options = options.unwrap_or(&default_options);
let opts_bsonc = match options.opts {
Some(ref o) => Some(Bsonc::from_document(o)?),
None => None
};
let mut error = BsoncError::empty();
let count = unsafe {
bindings::mongoc_collection_count_with_opts(
self.inner,
options.query_flags.flags(),
Bsonc::from_document(query)?.inner(),
options.skip as i64,
options.limit as i64,
match opts_bsonc {
Some(ref o) => o.inner(),
None => ptr::null()
},
match options.read_prefs {
Some(ref prefs) => prefs.inner(),
None => ptr::null()
},
error.mut_inner()
)
};
if error.is_empty() {
Ok(count)
} else {
Err(error.into())
}
}
pub fn create_bulk_operation(
&'a self,
options: Option<&BulkOperationOptions>
) -> BulkOperation<'a> {
assert!(!self.inner.is_null());
let default_options = BulkOperationOptions::default();
let options = options.unwrap_or(&default_options);
let inner = unsafe {
bindings::mongoc_collection_create_bulk_operation(
self.inner,
options.ordered as u8,
options.write_concern.inner()
)
};
BulkOperation::new(self, inner)
}
pub fn drop(&mut self) -> Result<()> {
assert!(!self.inner.is_null());
let mut error = BsoncError::empty();
let success = unsafe {
bindings::mongoc_collection_drop(
self.inner,
error.mut_inner()
)
};
if success == 0 {
assert!(!error.is_empty());
return Err(error.into())
}
Ok(())
}
pub fn find(
&'a self,
query: &Document,
options: Option<&CommandAndFindOptions>
) -> Result<Cursor<'a>> {
assert!(!self.inner.is_null());
let default_options = CommandAndFindOptions::default();
let options = options.unwrap_or(&default_options);
let fields_bsonc = options.fields_bsonc();
let cursor_ptr = unsafe {
bindings::mongoc_collection_find(
self.inner,
options.query_flags.flags(),
options.skip,
options.limit,
options.batch_size,
Bsonc::from_document(query)?.inner(),
match fields_bsonc {
Some(ref f) => f.inner(),
None => ptr::null()
},
match options.read_prefs {
Some(ref prefs) => prefs.inner(),
None => ptr::null()
}
)
};
if cursor_ptr.is_null() {
return Err(InvalidParamsError.into())
}
Ok(Cursor::new(
cursor::CreatedBy::Collection(self),
cursor_ptr,
fields_bsonc
))
}
pub fn find_and_modify(
&'a self,
query: &Document,
operation: FindAndModifyOperation<'a>,
options: Option<&FindAndModifyOptions>
) -> Result<Document> {
assert!(!self.inner.is_null());
let default_options = FindAndModifyOptions::default();
let options = options.unwrap_or(&default_options);
let fields_bsonc = options.fields_bsonc();
let mut reply = Bsonc::new();
let mut error = BsoncError::empty();
let sort_bsonc = match options.sort {
Some(ref doc) => {
Some(Bsonc::from_document(doc)?)
},
None => None
};
let update_bsonc = match operation {
FindAndModifyOperation::Update(ref doc) | FindAndModifyOperation::Upsert(ref doc) => {
Some(Bsonc::from_document(doc)?)
},
FindAndModifyOperation::Remove => None
};
let success = unsafe {
bindings::mongoc_collection_find_and_modify(
self.inner,
Bsonc::from_document(&query)?.inner(),
match sort_bsonc {
Some(ref s) => s.inner(),
None => ptr::null()
},
match update_bsonc {
Some(ref u) => u.inner(),
None => ptr::null()
},
match fields_bsonc {
Some(ref f) => f.inner(),
None => ptr::null()
},
match operation {
FindAndModifyOperation::Remove => true,
_ => false
} as u8,
match operation {
FindAndModifyOperation::Upsert(_) => true,
_ => false
} as u8,
options.new as u8,
reply.mut_inner(),
error.mut_inner()
)
};
if success == 1 {
match reply.as_document() {
Ok(document) => return Ok(document),
Err(error) => return Err(error.into())
}
} else {
Err(error.into())
}
}
pub fn get_name(&self) -> Cow<str> {
let cstr = unsafe {
CStr::from_ptr(bindings::mongoc_collection_get_name(self.inner))
};
String::from_utf8_lossy(cstr.to_bytes())
}
pub fn insert(
&'a self,
document: &Document,
options: Option<&InsertOptions>
) -> Result<()> {
assert!(!self.inner.is_null());
let default_options = InsertOptions::default();
let options = options.unwrap_or(&default_options);
let mut error = BsoncError::empty();
let success = unsafe {
bindings::mongoc_collection_insert(
self.inner,
options.insert_flags.flags(),
Bsonc::from_document(&document)?.inner(),
options.write_concern.inner(),
error.mut_inner()
)
};
if success == 1 {
Ok(())
} else {
Err(error.into())
}
}
pub fn remove(
&self,
selector: &Document,
options: Option<&RemoveOptions>
) -> Result<()> {
assert!(!self.inner.is_null());
let default_options = RemoveOptions::default();
let options = options.unwrap_or(&default_options);
let mut error = BsoncError::empty();
let success = unsafe {
bindings::mongoc_collection_remove(
self.inner,
options.remove_flags.flags(),
Bsonc::from_document(&selector)?.inner(),
options.write_concern.inner(),
error.mut_inner()
)
};
if success == 1 {
Ok(())
} else {
Err(error.into())
}
}
pub fn save(
&self,
document: &Document,
write_concern: Option<&WriteConcern>
) -> Result<()> {
assert!(!self.inner.is_null());
let default_write_concern = WriteConcern::default();
let write_concern = write_concern.unwrap_or(&default_write_concern);
let mut error = BsoncError::empty();
let success = unsafe {
bindings::mongoc_collection_save(
self.inner,
Bsonc::from_document(&document)?.inner(),
write_concern.inner(),
error.mut_inner()
)
};
if success == 1 {
Ok(())
} else {
Err(error.into())
}
}
pub fn update(
&self,
selector: &Document,
update: &Document,
options: Option<&UpdateOptions>
) -> Result<()> {
assert!(!self.inner.is_null());
let default_options = UpdateOptions::default();
let options = options.unwrap_or(&default_options);
let mut error = BsoncError::empty();
let success = unsafe {
bindings::mongoc_collection_update(
self.inner,
options.update_flags.flags(),
Bsonc::from_document(&selector)?.inner(),
Bsonc::from_document(&update)?.inner(),
options.write_concern.inner(),
error.mut_inner()
)
};
if success == 1 {
Ok(())
} else {
Err(error.into())
}
}
pub fn tail(
&'a self,
query: Document,
find_options: Option<CommandAndFindOptions>,
tail_options: Option<TailOptions>
) -> TailingCursor<'a> {
TailingCursor::new(
self,
query,
find_options.unwrap_or(CommandAndFindOptions::default()),
tail_options.unwrap_or(TailOptions::default())
)
}
}
impl<'a> Drop for Collection<'a> {
fn drop(&mut self) {
assert!(!self.inner.is_null());
unsafe {
bindings::mongoc_collection_destroy(self.inner);
}
}
}
pub struct BulkOperation<'a> {
_collection: &'a Collection<'a>,
inner: *mut bindings::mongoc_bulk_operation_t
}
impl<'a>BulkOperation<'a> {
fn new(
collection: &'a Collection<'a>,
inner: *mut bindings::mongoc_bulk_operation_t
) -> BulkOperation<'a> {
assert!(!inner.is_null());
BulkOperation {
_collection: collection,
inner: inner
}
}
pub fn insert(
&self,
document: &Document
) -> Result<()> {
assert!(!self.inner.is_null());
unsafe {
bindings::mongoc_bulk_operation_insert(
self.inner,
Bsonc::from_document(&document)?.inner()
)
}
Ok(())
}
pub fn remove(
&self,
selector: &Document
) -> Result<()> {
assert!(!self.inner.is_null());
unsafe {
bindings::mongoc_bulk_operation_remove(
self.inner,
Bsonc::from_document(&selector)?.inner()
)
}
Ok(())
}
pub fn remove_one(
&self,
selector: &Document
) -> Result<()> {
assert!(!self.inner.is_null());
unsafe {
bindings::mongoc_bulk_operation_remove_one(
self.inner,
Bsonc::from_document(&selector)?.inner()
)
}
Ok(())
}
pub fn replace_one(
&self,
selector: &Document,
document: &Document,
upsert: bool
) -> Result<()> {
assert!(!self.inner.is_null());
unsafe {
bindings::mongoc_bulk_operation_replace_one(
self.inner,
Bsonc::from_document(&selector)?.inner(),
Bsonc::from_document(&document)?.inner(),
upsert as u8
)
}
Ok(())
}
pub fn update_one(
&self,
selector: &Document,
document: &Document,
upsert: bool
) -> Result<()> {
assert!(!self.inner.is_null());
unsafe {
bindings::mongoc_bulk_operation_update_one(
self.inner,
Bsonc::from_document(&selector)?.inner(),
Bsonc::from_document(&document)?.inner(),
upsert as u8
)
}
Ok(())
}
pub fn update(
&self,
selector: &Document,
document: &Document,
upsert: bool
) -> Result<()> {
assert!(!self.inner.is_null());
unsafe {
bindings::mongoc_bulk_operation_update(
self.inner,
Bsonc::from_document(&selector)?.inner(),
Bsonc::from_document(&document)?.inner(),
upsert as u8
)
}
Ok(())
}
pub fn execute(self) -> BulkOperationResult<Document> {
let mut reply = Bsonc::new();
let mut error = BsoncError::empty();
let return_value = unsafe {
bindings::mongoc_bulk_operation_execute(
self.inner,
reply.mut_inner(),
error.mut_inner()
)
};
let document = match reply.as_document() {
Ok(document) => document,
Err(error) => return Err(BulkOperationError{error: error.into(), reply: doc!{}})
};
if return_value != 0 {
Ok(document)
} else {
Err(BulkOperationError{error: error.into(), reply: document})
}
}
}
impl<'a> Drop for BulkOperation<'a> {
fn drop(&mut self) {
assert!(!self.inner.is_null());
unsafe {
bindings::mongoc_bulk_operation_destroy(self.inner);
}
}
}