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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! This driver is a thin wrapper around the production-ready [Mongo C driver](https://github.com/mongodb/mongo-c-driver).
//!
//! It aims to provide a safe and ergonomic Rust interface which handles all the gnarly usage details of
//! the C driver for you. We use Rust's type system to make sure that we can only use the
//! underlying C driver in the recommended way specified in it's [documentation](http://mongoc.org/libmongoc/current/).
//!
//! To get started create a client pool wrapped in an `Arc` so we can share it between threads. Then pop a client from it
//! you can use to perform operations.
//!
//! # Example
//!
//! ```
//! use std::sync::Arc;
//! use mongo_driver::client::{ClientPool,Uri};
//!
//! let uri = Uri::new("mongodb://localhost:27017/").unwrap();
//! let pool = Arc::new(ClientPool::new(uri.clone(), None));
//! let client = pool.pop();
//! client.get_server_status(None).unwrap();
//! ```
//!
//! See the documentation for the available modules to find out how you can use the driver beyond
//! this.

extern crate libc;
extern crate mongoc_sys as mongoc;

#[macro_use]
extern crate bson;

#[macro_use]
extern crate log;

#[macro_use]
extern crate serde_derive;
extern crate serde;

use std::ffi::CStr;
use std::ptr;
use std::result;
use std::sync::{Once,ONCE_INIT};

use mongoc::bindings;

pub mod client;
pub mod collection;
pub mod cursor;
pub mod database;
pub mod flags;
pub mod read_prefs;
pub mod write_concern;

mod bsonc;
mod error;

pub use error::{MongoError,BsoncError,MongoErrorDomain,MongoErrorCode,InvalidParamsError,BulkOperationError};

/// Result that's used in all functions that perform operations on the database.
pub type Result<T> = result::Result<T, MongoError>;

/// Result that's used in bulk operations.
pub type BulkOperationResult<T> = result::Result<T, BulkOperationError>;

static MONGOC_INIT: Once = ONCE_INIT;

/// Init mongo driver, needs to be called once before doing
/// anything else.
fn init() {
    MONGOC_INIT.call_once(|| {
        unsafe {
            // Init mongoc subsystem
            bindings::mongoc_init();

            // Set mongoc log handler
            bindings::mongoc_log_set_handler(
                Some(mongoc_log_handler),
                ptr::null_mut()
            );
        }
    });
}

unsafe extern "C" fn mongoc_log_handler(
    log_level:  bindings::mongoc_log_level_t,
    log_domain: *const ::libc::c_char,
    message:    *const ::libc::c_char,
    _:          *mut ::libc::c_void
) {
    let log_domain_str = CStr::from_ptr(log_domain).to_string_lossy();
    let message_str = CStr::from_ptr(message).to_string_lossy();
    let log_line = format!("mongoc: {} - {}", log_domain_str, message_str);

    match log_level {
        bindings::MONGOC_LOG_LEVEL_ERROR    => error!("{}", log_line),
        bindings::MONGOC_LOG_LEVEL_CRITICAL => error!("{}", log_line),
        bindings::MONGOC_LOG_LEVEL_WARNING  => warn!("{}", log_line),
        bindings::MONGOC_LOG_LEVEL_MESSAGE  => info!("{}", log_line),
        bindings::MONGOC_LOG_LEVEL_INFO     => info!("{}", log_line),
        bindings::MONGOC_LOG_LEVEL_DEBUG    => debug!("{}", log_line),
        bindings::MONGOC_LOG_LEVEL_TRACE    => trace!("{}", log_line),
        _ => panic!("Unknown mongoc log level")
    }
}

/// Options to configure both command and find operations.
pub struct CommandAndFindOptions {
    /// Flags to use
    pub query_flags: flags::Flags<flags::QueryFlag>,
    /// Number of documents to skip, zero to ignore
    pub skip:        u32,
    /// Max number of documents to return, zero to ignore
    pub limit:       u32,
    /// Number of documents in each batch, zero to ignore (default is 100)
    pub batch_size:  u32,
    /// Fields to return, not all commands support this option
    pub fields:      Option<bson::Document>,
    /// Read prefs to use
    pub read_prefs:  Option<read_prefs::ReadPrefs>
}

impl CommandAndFindOptions {
    /// Default options used if none are provided.
    pub fn default() -> CommandAndFindOptions {
        CommandAndFindOptions {
            query_flags: flags::Flags::new(),
            skip:        0,
            limit:       0,
            batch_size:  0,
            fields:      None,
            read_prefs:  None
        }
    }

    pub fn with_fields(fields: bson::Document) -> CommandAndFindOptions {
        CommandAndFindOptions {
            query_flags: flags::Flags::new(),
            skip:        0,
            limit:       0,
            batch_size:  0,
            fields:      Some(fields),
            read_prefs:  None
        }
    }

    fn fields_bsonc(&self) -> Option<bsonc::Bsonc> {
        match self.fields {
            Some(ref f) => Some(bsonc::Bsonc::from_document(f).unwrap()),
            None => None
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_init() {
        super::init();
        super::init();
    }
}