Initial version
commit
d686ba240f
@ -0,0 +1,4 @@
|
|||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
|
mongo_c_driver_wrapper/mongo-c-driver*
|
||||||
|
mongo_c_driver_wrapper/src/bindings.rs
|
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "mongo_driver"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Thijs Cadier <thijs@appsignal.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "*"
|
||||||
|
|
||||||
|
[dependencies.bson]
|
||||||
|
git = "https://github.com/zonyitoo/bson-rs.git"
|
||||||
|
|
||||||
|
[dependencies.mongo_c_driver_wrapper]
|
||||||
|
path = "mongo_c_driver_wrapper"
|
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Thijs Cadier
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,21 @@
|
|||||||
|
# Mongo Rust Driver
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Mongo Rust driver built on top of the [Mongo C driver](https://github.com/mongodb/mongo-c-driver).
|
||||||
|
This drivers aims to be a thin wrapper around the production-ready C driver, while providing a safe
|
||||||
|
and ergonomic Rust interface that handles all the gnarly usage details of the C driver for you.
|
||||||
|
|
||||||
|
Bson encoding and decoding is handled by the [bson crate](https://github.com/zonyitoo/bson-rs), the bindings
|
||||||
|
are generated using [bindgen](https://github.com/crabtw/rust-bindgen).
|
||||||
|
|
||||||
|
The API is experimental, it might change at any time.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
The driver currently only builds on Unix, only tested on Mac Os X so far.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are very welcome, only the parts of the C driver we need have been wrapped so far. Please
|
||||||
|
write a test for any behavior you add.
|
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "mongo_c_driver_wrapper"
|
||||||
|
version = "1.1.8"
|
||||||
|
build = "build.rs"
|
||||||
|
authors = ["Thijs Cadier <thijs@appsignal.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "*"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
pkg-config = "*"
|
||||||
|
|
||||||
|
[build-dependencies.bindgen]
|
||||||
|
git = "https://github.com/crabtw/rust-bindgen.git"
|
@ -0,0 +1,86 @@
|
|||||||
|
#![feature(path_ext)]
|
||||||
|
|
||||||
|
extern crate bindgen;
|
||||||
|
extern crate pkg_config;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs::PathExt;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
static VERSION: &'static str = "1.1.8"; // Should be the same as the version in the manifest
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let current_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
let driver_path = format!(
|
||||||
|
"mongo-c-driver-{}",
|
||||||
|
VERSION
|
||||||
|
);
|
||||||
|
|
||||||
|
let libmongoc_path = Path::new(&out_dir).join("lib/libmongoc-1.0.a");
|
||||||
|
if !libmongoc_path.exists() { // TODO: This should check if we're at the right version
|
||||||
|
// Download and extract driver archive
|
||||||
|
let url = format!(
|
||||||
|
"https://github.com/mongodb/mongo-c-driver/releases/download/{}/mongo-c-driver-{}.tar.gz",
|
||||||
|
VERSION,
|
||||||
|
VERSION
|
||||||
|
);
|
||||||
|
assert!(Command::new("curl").arg("-O") // Save to disk
|
||||||
|
.arg("-L") // Follow redirects
|
||||||
|
.arg(url)
|
||||||
|
.status()
|
||||||
|
.unwrap()
|
||||||
|
.success());
|
||||||
|
|
||||||
|
let archive_name = format!(
|
||||||
|
"mongo-c-driver-{}.tar.gz",
|
||||||
|
VERSION
|
||||||
|
);
|
||||||
|
assert!(Command::new("tar").arg("xzf")
|
||||||
|
.arg(&archive_name)
|
||||||
|
.status()
|
||||||
|
.unwrap()
|
||||||
|
.success());
|
||||||
|
|
||||||
|
// Configure and install
|
||||||
|
assert!(Command::new("sh").arg("configure")
|
||||||
|
.arg("--enable-ssl=yes")
|
||||||
|
.arg("--enable-sasl=no")
|
||||||
|
.arg("--enable-static=yes")
|
||||||
|
.arg("--enable-shared=no")
|
||||||
|
.arg("--with-libbson=bundled")
|
||||||
|
.arg(format!("--prefix={}", &out_dir))
|
||||||
|
.current_dir(&driver_path)
|
||||||
|
.status()
|
||||||
|
.unwrap()
|
||||||
|
.success());
|
||||||
|
assert!(Command::new("make").current_dir(&driver_path).status().unwrap().success());
|
||||||
|
assert!(Command::new("make").arg("install").current_dir(&driver_path).status().unwrap().success());
|
||||||
|
|
||||||
|
// Generate bindings
|
||||||
|
let bindings_rs_path = Path::new(¤t_dir).join("src/bindings.rs");
|
||||||
|
let mongo_h_path = Path::new(¤t_dir).join(&driver_path).join("src/mongoc/mongoc.h");
|
||||||
|
let bson_path = Path::new(¤t_dir).join(&driver_path).join("src/libbson/src/bson");
|
||||||
|
bindgen::builder()
|
||||||
|
.emit_builtins()
|
||||||
|
.header(mongo_h_path.to_str().unwrap())
|
||||||
|
.clang_arg(format!("-I{}", bson_path.to_str().unwrap()))
|
||||||
|
.generate()
|
||||||
|
.unwrap()
|
||||||
|
.write_to_file(&bindings_rs_path)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output to Cargo
|
||||||
|
println!("cargo:root={}", &out_dir);
|
||||||
|
println!("cargo:libdir={}/lib", &out_dir);
|
||||||
|
println!("cargo:include={}/include", &out_dir);
|
||||||
|
println!("cargo:rustc-link-search={}/lib", &out_dir);
|
||||||
|
println!("cargo:rustc-link-lib=static=bson-1.0");
|
||||||
|
println!("cargo:rustc-link-lib=static=mongoc-1.0");
|
||||||
|
|
||||||
|
for link_path in pkg_config::find_library("openssl").unwrap().link_paths.iter(){
|
||||||
|
println!("cargo:rustc-link-search=framework={}", &link_path.display());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types,non_snake_case)]
|
||||||
|
pub mod bindings;
|
@ -0,0 +1,145 @@
|
|||||||
|
use std::ffi::{CStr,CString};
|
||||||
|
use std::ptr;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
|
use std::slice;
|
||||||
|
use libc::types::common::c95::c_void;
|
||||||
|
|
||||||
|
use super::BsoncError;
|
||||||
|
|
||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
use bson;
|
||||||
|
|
||||||
|
use super::Result;
|
||||||
|
|
||||||
|
pub struct Bsonc {
|
||||||
|
inner: *mut bindings::bson_t
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bsonc {
|
||||||
|
pub fn from_ptr(inner: *const bindings::bson_t) -> Bsonc {
|
||||||
|
assert!(!inner.is_null());
|
||||||
|
Bsonc { inner: inner as *mut bindings::bson_t }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_document(document: &bson::Document) -> Result<Bsonc> {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
try!(bson::encode_document(&mut buffer, document));
|
||||||
|
|
||||||
|
let inner = unsafe {
|
||||||
|
bindings::bson_new_from_data(
|
||||||
|
buffer[..].as_ptr(),
|
||||||
|
buffer.len() as u64
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inner will be null if there was an error converting the data.
|
||||||
|
// We're assuming the bson crate works and therefore assert here.
|
||||||
|
// See: http://api.mongodb.org/libbson/current/bson_new_from_data.html
|
||||||
|
assert!(!inner.is_null());
|
||||||
|
|
||||||
|
Ok(Bsonc{ inner: inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_json<S: Into<Vec<u8>>>(json: S) -> Result<Bsonc> {
|
||||||
|
let json_cstring = CString::new(json).unwrap();
|
||||||
|
let mut error = BsoncError::empty();
|
||||||
|
|
||||||
|
let inner = unsafe {
|
||||||
|
bindings::bson_new_from_json(
|
||||||
|
json_cstring.as_ptr() as *const u8,
|
||||||
|
json_cstring.as_bytes().len() as i64,
|
||||||
|
error.mut_inner()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if error.is_empty() {
|
||||||
|
Ok(Bsonc{ inner: inner })
|
||||||
|
} else {
|
||||||
|
Err(error.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_document(&self) -> Result<bson::Document> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
|
||||||
|
// This pointer should not be modified or freed
|
||||||
|
// See: http://api.mongodb.org/libbson/current/bson_get_data.html
|
||||||
|
let data_ptr = unsafe { bindings::bson_get_data(self.inner) };
|
||||||
|
assert!(!data_ptr.is_null());
|
||||||
|
|
||||||
|
let data_len = unsafe {
|
||||||
|
let bson = *self.inner;
|
||||||
|
bson.len
|
||||||
|
} as usize;
|
||||||
|
|
||||||
|
let mut slice = unsafe {
|
||||||
|
slice::from_raw_parts(data_ptr, data_len)
|
||||||
|
};
|
||||||
|
|
||||||
|
let document = try!(bson::decode_document(&mut slice));
|
||||||
|
Ok(document)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_json(&self) -> Cow<str> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
let json_ptr = unsafe { bindings::bson_as_json(self.inner, ptr::null_mut()) };
|
||||||
|
assert!(!json_ptr.is_null());
|
||||||
|
let json_cstr = unsafe { CStr::from_ptr(json_ptr) };
|
||||||
|
let out = String::from_utf8_lossy(json_cstr.to_bytes());
|
||||||
|
unsafe { bindings::bson_free(json_ptr as *mut c_void); }
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner(&self) -> *const bindings::bson_t {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Bsonc {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Bsonc: {}", self.as_json())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Bsonc {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
bindings::bson_destroy(self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use bson;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bsonc_from_and_as_document() {
|
||||||
|
let mut document = bson::Document::new();
|
||||||
|
document.insert("key".to_string(), bson::Bson::String("value".to_string()));
|
||||||
|
|
||||||
|
let bsonc = super::Bsonc::from_document(&document).unwrap();
|
||||||
|
|
||||||
|
let decoded = bsonc.as_document().unwrap();
|
||||||
|
assert!(decoded.contains_key("key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bsonc_from_and_as_json() {
|
||||||
|
let json = "{ \"key\" : \"value\" }";
|
||||||
|
let bsonc = super::Bsonc::from_json(json).unwrap();
|
||||||
|
assert_eq!(json.to_string(), bsonc.as_json().into_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_json() {
|
||||||
|
let malformed_json = "{ \"key\" : \"val }";
|
||||||
|
let bsonc_result = super::Bsonc::from_json(malformed_json);
|
||||||
|
|
||||||
|
assert!(bsonc_result.is_err());
|
||||||
|
|
||||||
|
let error_message = format!("{:?}", bsonc_result.err().unwrap());
|
||||||
|
assert!(error_message.starts_with("MongoError (BsoncError: parse error: premature EOF"));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
|
||||||
|
use super::uri::Uri;
|
||||||
|
use super::collection::Collection;
|
||||||
|
|
||||||
|
// TODO: We're using a sort of poor man's Arc here
|
||||||
|
// with this root bool, there must be a better way.
|
||||||
|
pub struct ClientPool {
|
||||||
|
root_instance: bool,
|
||||||
|
uri: Uri,
|
||||||
|
inner: *mut bindings::mongoc_client_pool_t
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientPool {
|
||||||
|
/// Create a new ClientPool
|
||||||
|
/// See: http://api.mongodb.org/c/current/mongoc_client_pool_t.html
|
||||||
|
pub fn new(uri: Uri) -> ClientPool {
|
||||||
|
let pool = unsafe {
|
||||||
|
let pool_ptr = bindings::mongoc_client_pool_new(uri.inner());
|
||||||
|
assert!(!pool_ptr.is_null());
|
||||||
|
pool_ptr
|
||||||
|
};
|
||||||
|
ClientPool {
|
||||||
|
root_instance: true,
|
||||||
|
uri: uri, // Become owner of uri so it doesn't go out of scope
|
||||||
|
inner: pool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a client from the client pool, possibly blocking until one is available.
|
||||||
|
/// See: http://api.mongodb.org/c/current/mongoc_client_pool_pop.html
|
||||||
|
pub fn pop(&self) -> Client {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
let client = unsafe { bindings::mongoc_client_pool_pop(self.inner) };
|
||||||
|
Client{
|
||||||
|
client_pool: self,
|
||||||
|
inner: client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a client back to the client pool, called from drop of client.
|
||||||
|
/// See: http://api.mongodb.org/c/current/mongoc_client_pool_push.html
|
||||||
|
unsafe fn push(&self, mongo_client: *mut bindings::mongoc_client_t) {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
bindings::mongoc_client_pool_push(
|
||||||
|
self.inner,
|
||||||
|
mongo_client
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for ClientPool { }
|
||||||
|
unsafe impl Sync for ClientPool { }
|
||||||
|
|
||||||
|
impl fmt::Debug for ClientPool {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "ClientPool for {}", self.uri.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for ClientPool {
|
||||||
|
fn clone(&self) -> ClientPool {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
ClientPool {
|
||||||
|
root_instance: false,
|
||||||
|
uri: self.uri.clone(),
|
||||||
|
inner: self.inner.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ClientPool {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.root_instance {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_client_pool_destroy(self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Client<'a> {
|
||||||
|
client_pool: &'a ClientPool,
|
||||||
|
inner: *mut bindings::mongoc_client_t
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Client<'a> {
|
||||||
|
pub fn get_collection<S: Into<Vec<u8>>>(&'a self, db: S, collection: S) -> Collection<'a> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
let mut coll;
|
||||||
|
unsafe {
|
||||||
|
let db_cstring = CString::new(db).unwrap();
|
||||||
|
let collection_cstring = CString::new(collection).unwrap();
|
||||||
|
|
||||||
|
coll = bindings::mongoc_client_get_collection(
|
||||||
|
self.inner,
|
||||||
|
db_cstring.as_ptr(),
|
||||||
|
collection_cstring.as_ptr()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Collection::new(self, coll)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for Client<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
self.client_pool.push(self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::thread;
|
||||||
|
use super::super::uri::Uri;
|
||||||
|
use super::super::client::ClientPool;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_pool_and_pop_client() {
|
||||||
|
super::super::init();
|
||||||
|
|
||||||
|
let uri = Uri::new("mongodb://localhost:27017/");
|
||||||
|
let pool = ClientPool::new(uri);
|
||||||
|
|
||||||
|
// Pop a client and insert a couple of times
|
||||||
|
for _ in 0..10 {
|
||||||
|
let client = pool.pop();
|
||||||
|
pool.pop();
|
||||||
|
client.get_collection("rust_test", "items");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_pool_and_pop_client_in_threads() {
|
||||||
|
super::super::init();
|
||||||
|
|
||||||
|
let uri = Uri::new("mongodb://localhost:27017/");
|
||||||
|
let pool = ClientPool::new(uri);
|
||||||
|
|
||||||
|
let pool1 = pool.clone();
|
||||||
|
let guard1 = thread::scoped(move || {
|
||||||
|
let client = pool1.pop();
|
||||||
|
client.get_collection("test", "items");
|
||||||
|
});
|
||||||
|
|
||||||
|
let pool2 = pool.clone();
|
||||||
|
let guard2 = thread::scoped(move || {
|
||||||
|
let client = pool2.pop();
|
||||||
|
client.get_collection("test", "items");
|
||||||
|
});
|
||||||
|
|
||||||
|
guard1.join();
|
||||||
|
guard2.join();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,379 @@
|
|||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
|
||||||
|
use bson::Document;
|
||||||
|
|
||||||
|
use super::Result;
|
||||||
|
use super::{BsoncError,InvalidParamsError};
|
||||||
|
use super::bsonc::Bsonc;
|
||||||
|
use super::client::Client;
|
||||||
|
use super::cursor;
|
||||||
|
use super::cursor::Cursor;
|
||||||
|
use super::flags::{Flags,FlagsValue,InsertFlag,QueryFlag,RemoveFlag};
|
||||||
|
use super::write_concern::WriteConcern;
|
||||||
|
use super::read_prefs::ReadPrefs;
|
||||||
|
|
||||||
|
pub enum CreatedBy<'a> {
|
||||||
|
Client(&'a Client<'a>)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Collection<'a> {
|
||||||
|
_created_by: CreatedBy<'a>,
|
||||||
|
inner: *mut bindings::mongoc_collection_t
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Collection<'a> {
|
||||||
|
pub fn new(
|
||||||
|
client: &'a Client<'a>,
|
||||||
|
inner: *mut bindings::mongoc_collection_t
|
||||||
|
) -> Collection<'a> {
|
||||||
|
assert!(!inner.is_null());
|
||||||
|
Collection {
|
||||||
|
_created_by: CreatedBy::Client(client),
|
||||||
|
inner: inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_with_options(
|
||||||
|
&self,
|
||||||
|
query_flags: &Flags<QueryFlag>,
|
||||||
|
query: &Document,
|
||||||
|
skip: u32,
|
||||||
|
limit: u32,
|
||||||
|
opts: Option<&Document>,
|
||||||
|
read_prefs: Option<&ReadPrefs>
|
||||||
|
) -> Result<i64> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
|
||||||
|
let mut error = BsoncError::empty();
|
||||||
|
let count = unsafe {
|
||||||
|
bindings::mongoc_collection_count_with_opts(
|
||||||
|
self.inner,
|
||||||
|
query_flags.flags(),
|
||||||
|
try!(Bsonc::from_document(query)).inner(),
|
||||||
|
skip as i64,
|
||||||
|
limit as i64,
|
||||||
|
match opts {
|
||||||
|
Some(o) => try!(Bsonc::from_document(o)).inner(),
|
||||||
|
None => ptr::null()
|
||||||
|
},
|
||||||
|
match read_prefs {
|
||||||
|
Some(prefs) => prefs.inner(),
|
||||||
|
None => ptr::null()
|
||||||
|
},
|
||||||
|
error.mut_inner()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if error.is_empty() {
|
||||||
|
Ok(count)
|
||||||
|
} else {
|
||||||
|
Err(error.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(
|
||||||
|
&self,
|
||||||
|
query: &Document
|
||||||
|
) -> Result<i64> {
|
||||||
|
self.count_with_options(
|
||||||
|
&Flags::new(),
|
||||||
|
query,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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_with_options(
|
||||||
|
&'a self,
|
||||||
|
query_flags: &Flags<QueryFlag>,
|
||||||
|
skip: u32,
|
||||||
|
limit: u32,
|
||||||
|
batch_size: u32,
|
||||||
|
query: &Document,
|
||||||
|
fields: Option<&Document>,
|
||||||
|
read_prefs: Option<&ReadPrefs>
|
||||||
|
) -> Result<Cursor<'a>> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
|
||||||
|
let inner = unsafe {
|
||||||
|
bindings::mongoc_collection_find(
|
||||||
|
self.inner,
|
||||||
|
query_flags.flags(),
|
||||||
|
skip,
|
||||||
|
limit,
|
||||||
|
batch_size,
|
||||||
|
try!(Bsonc::from_document(query)).inner(),
|
||||||
|
match fields {
|
||||||
|
Some(f) => {
|
||||||
|
try!(Bsonc::from_document(f)).inner()
|
||||||
|
},
|
||||||
|
None => ptr::null()
|
||||||
|
},
|
||||||
|
match read_prefs {
|
||||||
|
Some(prefs) => prefs.inner(),
|
||||||
|
None => ptr::null()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if inner.is_null() {
|
||||||
|
return Err(InvalidParamsError.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Cursor::new(cursor::CreatedBy::Collection(self), inner))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find(
|
||||||
|
&'a self,
|
||||||
|
query: &Document
|
||||||
|
) -> Result<Cursor<'a>> {
|
||||||
|
self.find_with_options(
|
||||||
|
&Flags::new(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&query,
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_with_options(
|
||||||
|
&'a self,
|
||||||
|
insert_flags: &Flags<InsertFlag>,
|
||||||
|
document: &Document,
|
||||||
|
write_concern: &WriteConcern
|
||||||
|
) -> Result<()> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
|
||||||
|
let mut error = BsoncError::empty();
|
||||||
|
let success = unsafe {
|
||||||
|
bindings::mongoc_collection_insert(
|
||||||
|
self.inner,
|
||||||
|
insert_flags.flags(),
|
||||||
|
try!(Bsonc::from_document(&document)).inner(),
|
||||||
|
write_concern.inner(),
|
||||||
|
error.mut_inner()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if success == 1 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(error.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&'a self, document: &Document) -> Result<()> {
|
||||||
|
self.insert_with_options(
|
||||||
|
&Flags::new(),
|
||||||
|
document,
|
||||||
|
&WriteConcern::new()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_with_options(
|
||||||
|
&self,
|
||||||
|
remove_flags: &Flags<RemoveFlag>,
|
||||||
|
selector: &Document,
|
||||||
|
write_concern: &WriteConcern
|
||||||
|
) -> Result<()> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
|
||||||
|
let mut error = BsoncError::empty();
|
||||||
|
let success = unsafe {
|
||||||
|
bindings::mongoc_collection_remove(
|
||||||
|
self.inner,
|
||||||
|
remove_flags.flags(),
|
||||||
|
try!(Bsonc::from_document(&selector)).inner(),
|
||||||
|
write_concern.inner(),
|
||||||
|
error.mut_inner()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if success == 1 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(error.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(
|
||||||
|
&self,
|
||||||
|
selector: &Document
|
||||||
|
) -> Result<()> {
|
||||||
|
self.remove_with_options(
|
||||||
|
&Flags::new(),
|
||||||
|
selector,
|
||||||
|
&WriteConcern::new()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_with_options(
|
||||||
|
&self,
|
||||||
|
document: &Document,
|
||||||
|
write_concern: &WriteConcern
|
||||||
|
) -> Result<()> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
|
||||||
|
let mut error = BsoncError::empty();
|
||||||
|
let success = unsafe {
|
||||||
|
bindings::mongoc_collection_save(
|
||||||
|
self.inner,
|
||||||
|
try!(Bsonc::from_document(&document)).inner(),
|
||||||
|
write_concern.inner(),
|
||||||
|
error.mut_inner()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if success == 1 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(error.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(
|
||||||
|
&self,
|
||||||
|
document: &Document,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.save_with_options(
|
||||||
|
document,
|
||||||
|
&WriteConcern::new()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for Collection<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_collection_destroy(self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use bson;
|
||||||
|
use super::super::uri::Uri;
|
||||||
|
use super::super::client::ClientPool;
|
||||||
|
use super::super::flags::{Flags};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mutation_and_finding() {
|
||||||
|
let uri = Uri::new("mongodb://localhost:27017/");
|
||||||
|
let pool = ClientPool::new(uri);
|
||||||
|
let client = pool.pop();
|
||||||
|
let mut collection = client.get_collection("rust_driver_test", "items");
|
||||||
|
collection.drop().unwrap_or(());
|
||||||
|
|
||||||
|
let mut document = bson::Document::new();
|
||||||
|
document.insert("key_1".to_string(), bson::Bson::String("Value 1".to_string()));
|
||||||
|
document.insert("key_2".to_string(), bson::Bson::String("Value 2".to_string()));
|
||||||
|
assert!(collection.insert(&document).is_ok());
|
||||||
|
|
||||||
|
let mut second_document = bson::Document::new();
|
||||||
|
second_document.insert("key_1".to_string(), bson::Bson::String("Value 3".to_string()));
|
||||||
|
assert!(collection.insert(&second_document).is_ok());
|
||||||
|
|
||||||
|
let query = bson::Document::new();
|
||||||
|
|
||||||
|
// Count the documents in the collection
|
||||||
|
assert_eq!(2, collection.count(&query).unwrap());
|
||||||
|
|
||||||
|
// Find the documents
|
||||||
|
assert_eq!(
|
||||||
|
collection.find(&document).unwrap().next().unwrap().unwrap().get("key_1").unwrap().to_json(),
|
||||||
|
bson::Bson::String("Value 1".to_string()).to_json()
|
||||||
|
);
|
||||||
|
let mut found_document = collection.find(&second_document).unwrap().next().unwrap().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
found_document.get("key_1").unwrap().to_json(),
|
||||||
|
bson::Bson::String("Value 3".to_string()).to_json()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the second document
|
||||||
|
found_document.insert("key_1".to_string(), bson::Bson::String("Value 4".to_string()));
|
||||||
|
assert!(collection.save(&found_document).is_ok());
|
||||||
|
|
||||||
|
// Reload and check value
|
||||||
|
let found_document = collection.find(&found_document).unwrap().next().unwrap().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
found_document.get("key_1").unwrap().to_json(),
|
||||||
|
bson::Bson::String("Value 4".to_string()).to_json()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove one
|
||||||
|
assert!(collection.remove(&found_document).is_ok());
|
||||||
|
|
||||||
|
// Count again
|
||||||
|
assert_eq!(1, collection.count(&query).unwrap());
|
||||||
|
|
||||||
|
// Find the document and see if it has the keys we expect
|
||||||
|
{
|
||||||
|
let mut cursor = collection.find(&query).unwrap();
|
||||||
|
let next_document = cursor.next().unwrap().unwrap();
|
||||||
|
assert!(next_document.contains_key("key_1"));
|
||||||
|
assert!(next_document.contains_key("key_2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the document with fields set
|
||||||
|
let mut fields = bson::Document::new();
|
||||||
|
fields.insert("key_1".to_string(), bson::Bson::Boolean(true));
|
||||||
|
{
|
||||||
|
let mut cursor = collection.find_with_options(
|
||||||
|
&Flags::new(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&query,
|
||||||
|
Some(&fields),
|
||||||
|
None
|
||||||
|
).unwrap();
|
||||||
|
let next_document = cursor.next().unwrap().unwrap();
|
||||||
|
assert!(next_document.contains_key("key_1"));
|
||||||
|
assert!(!next_document.contains_key("key_2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop collection
|
||||||
|
collection.drop().unwrap();
|
||||||
|
assert_eq!(0, collection.count(&query).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insert_failure() {
|
||||||
|
let uri = Uri::new("mongodb://localhost:27018/"); // There should be no mongo server here
|
||||||
|
let pool = ClientPool::new(uri);
|
||||||
|
let client = pool.pop();
|
||||||
|
let collection = client.get_collection("rust_driver_test", "items");
|
||||||
|
let document = bson::Document::new();
|
||||||
|
|
||||||
|
let result = collection.insert(&document);
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
"MongoError (BsoncError: Failed to connect to target host: localhost:27018)",
|
||||||
|
format!("{:?}", result.err().unwrap())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
use std::iter::Iterator;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
use bson::Document;
|
||||||
|
|
||||||
|
use super::BsoncError;
|
||||||
|
use super::bsonc;
|
||||||
|
use super::client::Client;
|
||||||
|
use super::collection::Collection;
|
||||||
|
|
||||||
|
use super::Result;
|
||||||
|
|
||||||
|
pub enum CreatedBy<'a> {
|
||||||
|
Collection(&'a Collection<'a>),
|
||||||
|
Client(&'a Client<'a>)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cursor<'a> {
|
||||||
|
_created_by: CreatedBy<'a>,
|
||||||
|
inner: *mut bindings::mongoc_cursor_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Cursor<'a> {
|
||||||
|
pub fn new(
|
||||||
|
created_by: CreatedBy<'a>,
|
||||||
|
inner: *mut bindings::mongoc_cursor_t
|
||||||
|
) -> Cursor<'a> {
|
||||||
|
assert!(!inner.is_null());
|
||||||
|
Cursor {
|
||||||
|
_created_by: created_by,
|
||||||
|
inner: inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_alive(&self) -> bool {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_cursor_is_alive(self.inner) == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn more(&self) -> bool {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_cursor_more(self.inner) == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error(&self) -> BsoncError {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
let mut error = BsoncError::empty();
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_cursor_error(
|
||||||
|
self.inner,
|
||||||
|
error.mut_inner()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for Cursor<'a> {
|
||||||
|
type Item = Result<Document>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
|
||||||
|
// The C driver writes the document to memory and sets an
|
||||||
|
// already existing pointer to it.
|
||||||
|
let mut bson_ptr: *const bindings::bson_t = ptr::null();
|
||||||
|
let success = unsafe {
|
||||||
|
bindings::mongoc_cursor_next(
|
||||||
|
self.inner,
|
||||||
|
&mut bson_ptr
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if success == 0 {
|
||||||
|
let error = self.error();
|
||||||
|
if error.is_empty() {
|
||||||
|
return None
|
||||||
|
} else {
|
||||||
|
return Some(Err(error.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(!bson_ptr.is_null());
|
||||||
|
|
||||||
|
let bsonc = bsonc::Bsonc::from_ptr(bson_ptr);
|
||||||
|
let document = bsonc.as_document();
|
||||||
|
match document {
|
||||||
|
Ok(document) => Some(Ok(document)),
|
||||||
|
Err(error) => Some(Err(error.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for Cursor<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_cursor_destroy(self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use bson;
|
||||||
|
use super::super::uri::Uri;
|
||||||
|
use super::super::client::ClientPool;
|
||||||
|
use super::super::Result;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cursor() {
|
||||||
|
let uri = Uri::new("mongodb://localhost:27017/");
|
||||||
|
let pool = ClientPool::new(uri);
|
||||||
|
let client = pool.pop();
|
||||||
|
let mut collection = client.get_collection("rust_driver_test", "cursor_items");
|
||||||
|
|
||||||
|
let mut document = bson::Document::new();
|
||||||
|
document.insert("key".to_string(), bson::Bson::String("value".to_string()));
|
||||||
|
|
||||||
|
collection.drop().unwrap();
|
||||||
|
for _ in 0..10 {
|
||||||
|
assert!(collection.insert(&document).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = bson::Document::new();
|
||||||
|
let cursor = collection.find(&query).unwrap();
|
||||||
|
|
||||||
|
assert!(cursor.is_alive());
|
||||||
|
|
||||||
|
let documents = cursor.into_iter().collect::<Vec<Result<bson::Document>>>();
|
||||||
|
|
||||||
|
// See if we got 10 results and the iterator then stopped
|
||||||
|
assert_eq!(10, documents.len());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,296 @@
|
|||||||
|
use std::error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
|
use bson::{DecoderError,EncoderError};
|
||||||
|
|
||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
|
||||||
|
pub enum MongoError {
|
||||||
|
Bsonc(BsoncError),
|
||||||
|
Decoder(DecoderError),
|
||||||
|
Encoder(EncoderError),
|
||||||
|
InvalidParams(InvalidParamsError)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MongoError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
MongoError::Bsonc(ref err) => write!(f, "{}", err),
|
||||||
|
MongoError::Encoder(ref err) => write!(f, "{}", err),
|
||||||
|
MongoError::Decoder(ref err) => write!(f, "{}", err),
|
||||||
|
MongoError::InvalidParams(ref err) => write!(f, "{}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for MongoError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
MongoError::Bsonc(ref err) => write!(f, "MongoError ({:?})", err),
|
||||||
|
MongoError::Decoder(ref err) => write!(f, "MongoError ({:?})", err),
|
||||||
|
MongoError::Encoder(ref err) => write!(f, "MongoError ({:?})", err),
|
||||||
|
MongoError::InvalidParams(ref err) => write!(f, "MongoError ({:?})", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for MongoError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
MongoError::Bsonc(ref err) => err.description(),
|
||||||
|
MongoError::Decoder(ref err) => err.description(),
|
||||||
|
MongoError::Encoder(ref err) => err.description(),
|
||||||
|
MongoError::InvalidParams(ref err) => err.description()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&error::Error> {
|
||||||
|
match *self {
|
||||||
|
MongoError::Bsonc(ref err) => Some(err),
|
||||||
|
MongoError::Decoder(ref err) => Some(err),
|
||||||
|
MongoError::Encoder(ref err) => Some(err),
|
||||||
|
MongoError::InvalidParams(ref err) => Some(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DecoderError> for MongoError {
|
||||||
|
fn from(error: DecoderError) -> MongoError {
|
||||||
|
MongoError::Decoder(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EncoderError> for MongoError {
|
||||||
|
fn from(error: EncoderError) -> MongoError {
|
||||||
|
MongoError::Encoder(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BsoncError {
|
||||||
|
inner: bindings::bson_error_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,PartialEq)]
|
||||||
|
pub enum MongoErrorDomain {
|
||||||
|
Blank,
|
||||||
|
Client,
|
||||||
|
Stream,
|
||||||
|
Protocol,
|
||||||
|
Cursor,
|
||||||
|
Query,
|
||||||
|
Insert,
|
||||||
|
Sasl,
|
||||||
|
Bson,
|
||||||
|
Matcher,
|
||||||
|
Namespace,
|
||||||
|
Command,
|
||||||
|
Collection,
|
||||||
|
Gridfs,
|
||||||
|
Scram,
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,PartialEq)]
|
||||||
|
pub enum MongoErrorCode {
|
||||||
|
Blank,
|
||||||
|
StreamInvalidType,
|
||||||
|
StreamInvalidState,
|
||||||
|
StreamNameResolution,
|
||||||
|
StreamSocket,
|
||||||
|
StreamConnect,
|
||||||
|
StreamNotEstablished,
|
||||||
|
ClientNotReady,
|
||||||
|
ClientTooBig,
|
||||||
|
ClientTooSmall,
|
||||||
|
ClientGetnonce,
|
||||||
|
ClientAuthenticate,
|
||||||
|
ClientNoAcceptablePeer,
|
||||||
|
ClientInExhaust,
|
||||||
|
ProtocolInvalidReply,
|
||||||
|
ProtocolBadWireVersion,
|
||||||
|
CursorInvalidCursor,
|
||||||
|
QueryFailure,
|
||||||
|
BsonInvalid,
|
||||||
|
MatcherInvalid,
|
||||||
|
NamespaceInvalid,
|
||||||
|
NamespaceInvalidFilterType,
|
||||||
|
CommandInvalidArg,
|
||||||
|
CollectionInsertFailed,
|
||||||
|
CollectionUpdateFailed,
|
||||||
|
CollectionDeleteFailed,
|
||||||
|
CollectionDoesNotExist,
|
||||||
|
GridfsInvalidFilename,
|
||||||
|
ScramNotDone,
|
||||||
|
ScramProtocolError,
|
||||||
|
QueryCommandNotFound,
|
||||||
|
QueryNotTailable,
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BsoncError {
|
||||||
|
pub fn empty() -> BsoncError {
|
||||||
|
BsoncError {
|
||||||
|
inner: bindings::bson_error_t {
|
||||||
|
domain: 0,
|
||||||
|
code: 0,
|
||||||
|
message: [0; 504]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.inner.domain == 0 && self.inner.code == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn domain(&self) -> MongoErrorDomain {
|
||||||
|
match self.inner.domain {
|
||||||
|
0 => MongoErrorDomain::Blank,
|
||||||
|
bindings::MONGOC_ERROR_CLIENT => MongoErrorDomain::Client,
|
||||||
|
bindings::MONGOC_ERROR_STREAM => MongoErrorDomain::Stream,
|
||||||
|
bindings::MONGOC_ERROR_PROTOCOL => MongoErrorDomain::Protocol,
|
||||||
|
bindings::MONGOC_ERROR_CURSOR => MongoErrorDomain::Cursor,
|
||||||
|
bindings::MONGOC_ERROR_QUERY => MongoErrorDomain::Query,
|
||||||
|
bindings::MONGOC_ERROR_INSERT => MongoErrorDomain::Insert,
|
||||||
|
bindings::MONGOC_ERROR_SASL => MongoErrorDomain::Sasl,
|
||||||
|
bindings::MONGOC_ERROR_BSON => MongoErrorDomain::Bson,
|
||||||
|
bindings::MONGOC_ERROR_MATCHER => MongoErrorDomain::Matcher,
|
||||||
|
bindings::MONGOC_ERROR_NAMESPACE => MongoErrorDomain::Namespace,
|
||||||
|
bindings::MONGOC_ERROR_COMMAND => MongoErrorDomain::Command,
|
||||||
|
bindings::MONGOC_ERROR_COLLECTION => MongoErrorDomain::Collection,
|
||||||
|
bindings::MONGOC_ERROR_GRIDFS => MongoErrorDomain::Gridfs,
|
||||||
|
bindings::MONGOC_ERROR_SCRAM => MongoErrorDomain::Scram,
|
||||||
|
_ => MongoErrorDomain::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn code(&self) -> MongoErrorCode {
|
||||||
|
match self.inner.code {
|
||||||
|
0 => MongoErrorCode::Blank,
|
||||||
|
bindings::MONGOC_ERROR_STREAM_INVALID_TYPE => MongoErrorCode::StreamInvalidType,
|
||||||
|
bindings::MONGOC_ERROR_STREAM_INVALID_STATE => MongoErrorCode::StreamInvalidState,
|
||||||
|
bindings::MONGOC_ERROR_STREAM_NAME_RESOLUTION => MongoErrorCode::StreamNameResolution,
|
||||||
|
bindings::MONGOC_ERROR_STREAM_SOCKET => MongoErrorCode::StreamSocket,
|
||||||
|
bindings::MONGOC_ERROR_STREAM_CONNECT => MongoErrorCode::StreamConnect,
|
||||||
|
bindings::MONGOC_ERROR_STREAM_NOT_ESTABLISHED => MongoErrorCode::StreamNotEstablished,
|
||||||
|
bindings::MONGOC_ERROR_CLIENT_NOT_READY => MongoErrorCode::ClientNotReady,
|
||||||
|
bindings::MONGOC_ERROR_CLIENT_TOO_BIG => MongoErrorCode::ClientTooBig,
|
||||||
|
bindings::MONGOC_ERROR_CLIENT_TOO_SMALL => MongoErrorCode::ClientTooSmall,
|
||||||
|
bindings::MONGOC_ERROR_CLIENT_GETNONCE => MongoErrorCode::ClientGetnonce,
|
||||||
|
bindings::MONGOC_ERROR_CLIENT_AUTHENTICATE => MongoErrorCode::ClientAuthenticate,
|
||||||
|
bindings::MONGOC_ERROR_CLIENT_NO_ACCEPTABLE_PEER => MongoErrorCode::ClientNoAcceptablePeer,
|
||||||
|
bindings::MONGOC_ERROR_CLIENT_IN_EXHAUST => MongoErrorCode::ClientInExhaust,
|
||||||
|
bindings::MONGOC_ERROR_PROTOCOL_INVALID_REPLY => MongoErrorCode::ProtocolInvalidReply,
|
||||||
|
bindings::MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION => MongoErrorCode::ProtocolBadWireVersion,
|
||||||
|
bindings::MONGOC_ERROR_CURSOR_INVALID_CURSOR => MongoErrorCode::CursorInvalidCursor,
|
||||||
|
bindings::MONGOC_ERROR_QUERY_FAILURE => MongoErrorCode::QueryFailure,
|
||||||
|
bindings::MONGOC_ERROR_BSON_INVALID => MongoErrorCode::BsonInvalid,
|
||||||
|
bindings::MONGOC_ERROR_MATCHER_INVALID => MongoErrorCode::MatcherInvalid,
|
||||||
|
bindings::MONGOC_ERROR_NAMESPACE_INVALID => MongoErrorCode::NamespaceInvalid,
|
||||||
|
bindings::MONGOC_ERROR_NAMESPACE_INVALID_FILTER_TYPE => MongoErrorCode::NamespaceInvalidFilterType,
|
||||||
|
bindings::MONGOC_ERROR_COMMAND_INVALID_ARG => MongoErrorCode::CommandInvalidArg,
|
||||||
|
bindings::MONGOC_ERROR_COLLECTION_INSERT_FAILED => MongoErrorCode::CollectionInsertFailed,
|
||||||
|
bindings::MONGOC_ERROR_COLLECTION_UPDATE_FAILED => MongoErrorCode::CollectionUpdateFailed,
|
||||||
|
bindings::MONGOC_ERROR_COLLECTION_DELETE_FAILED => MongoErrorCode::CollectionDeleteFailed,
|
||||||
|
bindings::MONGOC_ERROR_COLLECTION_DOES_NOT_EXIST => MongoErrorCode::CollectionDoesNotExist,
|
||||||
|
bindings::MONGOC_ERROR_GRIDFS_INVALID_FILENAME => MongoErrorCode::GridfsInvalidFilename,
|
||||||
|
bindings::MONGOC_ERROR_SCRAM_NOT_DONE => MongoErrorCode::ScramNotDone,
|
||||||
|
bindings::MONGOC_ERROR_SCRAM_PROTOCOL_ERROR => MongoErrorCode::ScramProtocolError,
|
||||||
|
bindings::MONGOC_ERROR_QUERY_COMMAND_NOT_FOUND => MongoErrorCode::QueryCommandNotFound,
|
||||||
|
bindings::MONGOC_ERROR_QUERY_NOT_TAILABLE => MongoErrorCode::QueryNotTailable,
|
||||||
|
_ => MongoErrorCode::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_message(&self) -> Cow<str> {
|
||||||
|
let cstr = unsafe { CStr::from_ptr(&self.inner.message as *const i8) };
|
||||||
|
String::from_utf8_lossy(cstr.to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mut_inner(&mut self) -> &mut bindings::bson_error_t {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for BsoncError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "BsoncError: {}", &self.get_message())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BsoncError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", &self.get_message())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for BsoncError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"Error reported by the underlying Mongo C driver"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BsoncError> for MongoError {
|
||||||
|
fn from(error: BsoncError) -> MongoError {
|
||||||
|
MongoError::Bsonc(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InvalidParamsError;
|
||||||
|
|
||||||
|
impl fmt::Debug for InvalidParamsError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "InvalidParamsError: Invalid params supplied")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for InvalidParamsError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Invalid params supplied")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for InvalidParamsError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"Invalid params reported by the underlying Mongo C driver, no more information is available"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<InvalidParamsError> for MongoError {
|
||||||
|
fn from(error: InvalidParamsError) -> MongoError {
|
||||||
|
MongoError::InvalidParams(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{BsoncError,MongoErrorDomain,MongoErrorCode};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bson_error_empty() {
|
||||||
|
let mut error = BsoncError::empty();
|
||||||
|
assert!(error.is_empty());
|
||||||
|
error.mut_inner().code = 1;
|
||||||
|
assert!(!error.is_empty());
|
||||||
|
error.mut_inner().domain = 1;
|
||||||
|
error.mut_inner().code = 0;
|
||||||
|
assert!(!error.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bson_error_domain() {
|
||||||
|
let mut error = BsoncError::empty();
|
||||||
|
assert_eq!(MongoErrorDomain::Blank, error.domain());
|
||||||
|
error.mut_inner().domain = 1;
|
||||||
|
assert_eq!(MongoErrorDomain::Client, error.domain());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bson_error_code() {
|
||||||
|
let mut error = BsoncError::empty();
|
||||||
|
assert_eq!(MongoErrorCode::Blank, error.code());
|
||||||
|
error.mut_inner().code = 1;
|
||||||
|
assert_eq!(MongoErrorCode::StreamInvalidType, error.code());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
|
||||||
|
pub struct Flags<T> {
|
||||||
|
flags: Vec<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T> Flags<T> {
|
||||||
|
pub fn new() -> Flags<T> {
|
||||||
|
Flags {
|
||||||
|
flags: Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, flag: T) {
|
||||||
|
self.flags.push(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FlagsValue {
|
||||||
|
fn flags(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flags for insert operations
|
||||||
|
/// See: http://api.mongodb.org/c/current/mongoc_insert_flags_t.html
|
||||||
|
pub enum InsertFlag {
|
||||||
|
ContinueOnError,
|
||||||
|
NoValidate
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlagsValue for Flags<InsertFlag> {
|
||||||
|
fn flags(&self) -> u32 {
|
||||||
|
if self.flags.is_empty() {
|
||||||
|
bindings::MONGOC_INSERT_NONE
|
||||||
|
} else {
|
||||||
|
self.flags.iter().fold(0, { |flags, flag|
|
||||||
|
flags | match flag {
|
||||||
|
&InsertFlag::ContinueOnError => bindings::MONGOC_INSERT_CONTINUE_ON_ERROR,
|
||||||
|
&InsertFlag::NoValidate => 1 | 31 // MONGOC_INSERT_NO_VALIDATE defined in macro
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flags for query operations
|
||||||
|
/// See: http://api.mongodb.org/c/current/mongoc_query_flags_t.html
|
||||||
|
pub enum QueryFlag {
|
||||||
|
TailableCursor,
|
||||||
|
SlaveOk,
|
||||||
|
OplogReplay,
|
||||||
|
NoCursorTimeout,
|
||||||
|
AwaitData,
|
||||||
|
Exhaust,
|
||||||
|
Partial
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlagsValue for Flags<QueryFlag> {
|
||||||
|
fn flags(&self) -> u32 {
|
||||||
|
if self.flags.is_empty() {
|
||||||
|
bindings::MONGOC_QUERY_NONE
|
||||||
|
} else {
|
||||||
|
self.flags.iter().fold(0, { |flags, flag|
|
||||||
|
flags | match flag {
|
||||||
|
&QueryFlag::TailableCursor => bindings::MONGOC_QUERY_TAILABLE_CURSOR,
|
||||||
|
&QueryFlag::SlaveOk => bindings::MONGOC_QUERY_SLAVE_OK,
|
||||||
|
&QueryFlag::OplogReplay => bindings::MONGOC_QUERY_OPLOG_REPLAY,
|
||||||
|
&QueryFlag::NoCursorTimeout => bindings::MONGOC_QUERY_NO_CURSOR_TIMEOUT,
|
||||||
|
&QueryFlag::AwaitData => bindings::MONGOC_QUERY_AWAIT_DATA,
|
||||||
|
&QueryFlag::Exhaust => bindings::MONGOC_QUERY_EXHAUST,
|
||||||
|
&QueryFlag::Partial => bindings::MONGOC_QUERY_PARTIAL
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flags for deletion operations
|
||||||
|
/// See: http://api.mongodb.org/c/1.1.8/mongoc_remove_flags_t.html
|
||||||
|
pub enum RemoveFlag {
|
||||||
|
SingleRemove
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlagsValue for Flags<RemoveFlag> {
|
||||||
|
fn flags(&self) -> u32 {
|
||||||
|
if self.flags.is_empty() {
|
||||||
|
bindings::MONGOC_REMOVE_NONE
|
||||||
|
} else {
|
||||||
|
bindings::MONGOC_REMOVE_SINGLE_REMOVE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::FlagsValue;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_insert_flags() {
|
||||||
|
let mut flags = super::Flags::new();
|
||||||
|
assert_eq!(0, flags.flags());
|
||||||
|
|
||||||
|
flags.add(super::InsertFlag::ContinueOnError);
|
||||||
|
assert_eq!(1, flags.flags());
|
||||||
|
|
||||||
|
flags.add(super::InsertFlag::NoValidate);
|
||||||
|
assert_eq!(31, flags.flags());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_query_flags() {
|
||||||
|
let mut flags = super::Flags::new();
|
||||||
|
assert_eq!(0, flags.flags());
|
||||||
|
|
||||||
|
flags.add(super::QueryFlag::TailableCursor);
|
||||||
|
assert_eq!(2, flags.flags());
|
||||||
|
|
||||||
|
flags.add(super::QueryFlag::Partial);
|
||||||
|
assert_eq!(130, flags.flags());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
#![feature(scoped)]
|
||||||
|
|
||||||
|
extern crate libc;
|
||||||
|
extern crate mongo_c_driver_wrapper;
|
||||||
|
extern crate bson;
|
||||||
|
|
||||||
|
use std::result;
|
||||||
|
|
||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
|
||||||
|
pub mod bsonc;
|
||||||
|
pub mod client;
|
||||||
|
pub mod collection;
|
||||||
|
pub mod cursor;
|
||||||
|
pub mod error;
|
||||||
|
pub mod flags;
|
||||||
|
pub mod read_prefs;
|
||||||
|
pub mod uri;
|
||||||
|
pub mod write_concern;
|
||||||
|
|
||||||
|
pub use error::{MongoError,BsoncError,InvalidParamsError};
|
||||||
|
|
||||||
|
pub type Result<T> = result::Result<T, MongoError>;
|
||||||
|
|
||||||
|
static mut INITIALIZED: bool = false;
|
||||||
|
|
||||||
|
/// Init mongo driver, needs to be called once before doing
|
||||||
|
/// anything else.
|
||||||
|
pub fn init() {
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_init();
|
||||||
|
INITIALIZED = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clean up mongo driver's resources
|
||||||
|
pub fn cleanup() {
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_cleanup();
|
||||||
|
INITIALIZED = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_initialized() -> bool {
|
||||||
|
unsafe { INITIALIZED }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn test_init_and_cleanup() {
|
||||||
|
super::init();
|
||||||
|
assert!(super::is_initialized());
|
||||||
|
|
||||||
|
super::cleanup();
|
||||||
|
assert!(!super::is_initialized());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
|
||||||
|
pub enum ReadMode {
|
||||||
|
Primary,
|
||||||
|
Secondary,
|
||||||
|
PrimaryPreferred,
|
||||||
|
SecondaryPreferred,
|
||||||
|
Nearest
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_mode_value(read_mode: &ReadMode) -> bindings::mongoc_read_mode_t {
|
||||||
|
match read_mode {
|
||||||
|
&ReadMode::Primary => bindings::MONGOC_READ_PRIMARY,
|
||||||
|
&ReadMode::Secondary => bindings::MONGOC_READ_SECONDARY,
|
||||||
|
&ReadMode::PrimaryPreferred => bindings::MONGOC_READ_PRIMARY_PREFERRED,
|
||||||
|
&ReadMode::SecondaryPreferred => bindings::MONGOC_READ_SECONDARY_PREFERRED,
|
||||||
|
&ReadMode::Nearest => bindings::MONGOC_READ_NEAREST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReadPrefs {
|
||||||
|
inner: *mut bindings::mongoc_read_prefs_t
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadPrefs {
|
||||||
|
pub fn new(read_mode: &ReadMode) -> ReadPrefs {
|
||||||
|
let read_mode_value = read_mode_value(read_mode);
|
||||||
|
let inner = unsafe { bindings::mongoc_read_prefs_new(read_mode_value) };
|
||||||
|
assert!(!inner.is_null());
|
||||||
|
ReadPrefs { inner: inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default() -> ReadPrefs{
|
||||||
|
ReadPrefs::new(&ReadMode::Primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner(&self) -> *const bindings::mongoc_read_prefs_t {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ReadPrefs {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_read_prefs_destroy(self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn test_read_prefs() {
|
||||||
|
let read_prefs = super::ReadPrefs::default();
|
||||||
|
assert!(!read_prefs.inner().is_null());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
use std::ffi::{CStr,CString};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
|
||||||
|
/// Abstraction on top of MongoDB connection URI format.
|
||||||
|
/// See: http://api.mongodb.org/c/current/mongoc_uri_t.html
|
||||||
|
|
||||||
|
pub struct Uri {
|
||||||
|
inner: *mut bindings::mongoc_uri_t
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Uri {
|
||||||
|
pub fn new<T: Into<Vec<u8>>>(uri_string: T) -> Uri {
|
||||||
|
let uri_cstring = CString::new(uri_string).unwrap();
|
||||||
|
let uri = unsafe { bindings::mongoc_uri_new(uri_cstring.as_ptr()) };
|
||||||
|
Uri {
|
||||||
|
inner: uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn inner(&self) -> *const bindings::mongoc_uri_t {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str<'a>(&'a self) -> Cow<'a, str> {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
let cstr = CStr::from_ptr(
|
||||||
|
bindings::mongoc_uri_get_string(self.inner)
|
||||||
|
);
|
||||||
|
String::from_utf8_lossy(cstr.to_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add various methods that are available on uri
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Uri {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Uri {
|
||||||
|
fn clone(&self) -> Uri {
|
||||||
|
Uri::new(self.as_str().into_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Uri {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_uri_destroy(self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn test_new_uri() {
|
||||||
|
let uri = super::Uri::new("mongodb://localhost:27017/");
|
||||||
|
assert_eq!("mongodb://localhost:27017/", uri.as_str());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
use mongo_c_driver_wrapper::bindings;
|
||||||
|
|
||||||
|
pub struct WriteConcern {
|
||||||
|
inner: *mut bindings::mongoc_write_concern_t
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WriteConcern {
|
||||||
|
pub fn new() -> WriteConcern {
|
||||||
|
let inner = unsafe { bindings::mongoc_write_concern_new() };
|
||||||
|
assert!(!inner.is_null());
|
||||||
|
WriteConcern { inner: inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner(&self) -> *const bindings::mongoc_write_concern_t {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WriteConcern {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(!self.inner.is_null());
|
||||||
|
unsafe {
|
||||||
|
bindings::mongoc_write_concern_destroy(self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn test_write_concern() {
|
||||||
|
let write_concern = super::WriteConcern::new();
|
||||||
|
assert!(!write_concern.inner().is_null());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue