pub mod options;
pub mod roles;
use auth::Authenticator;
use bson::{self, bson, doc, Bson};
use {Client, CommandType, ThreadedClient, Result};
use Error::{CursorNotFoundError, OperationError, ResponseError};
use coll::Collection;
use coll::options::FindOptions;
use common::{ReadPreference, merge_options, WriteConcern};
use cursor::{Cursor, DEFAULT_BATCH_SIZE};
use self::options::{CreateCollectionOptions, CreateUserOptions, UserInfoOptions};
use semver::Version;
use std::error::Error;
use std::sync::Arc;
#[derive(Debug)]
pub struct DatabaseInner {
pub name: String,
pub client: Client,
pub read_preference: ReadPreference,
pub write_concern: WriteConcern,
}
pub type Database = Arc<DatabaseInner>;
pub trait ThreadedDatabase {
fn open(
client: Client,
name: &str,
read_preference: Option<ReadPreference>,
write_concern: Option<WriteConcern>,
) -> Database;
fn version(&self) -> Result<Version>;
fn auth(&self, user: &str, password: &str) -> Result<()>;
fn collection(&self, coll_name: &str) -> Collection;
fn collection_with_prefs(
&self,
coll_name: &str,
create: bool,
read_preference: Option<ReadPreference>,
write_concern: Option<WriteConcern>,
) -> Collection;
fn get_req_id(&self) -> i32;
fn command_cursor(
&self,
spec: bson::Document,
cmd_type: CommandType,
read_pref: ReadPreference,
) -> Result<Cursor>;
fn command(
&self,
spec: bson::Document,
cmd_type: CommandType,
read_preference: Option<ReadPreference>,
) -> Result<bson::Document>;
fn list_collections(&self, filter: Option<bson::Document>) -> Result<Cursor>;
fn list_collections_with_batch_size(
&self,
filter: Option<bson::Document>,
batch_size: i32,
) -> Result<Cursor>;
fn collection_names(&self, filter: Option<bson::Document>) -> Result<Vec<String>>;
fn create_collection(&self, name: &str, options: Option<CreateCollectionOptions>)
-> Result<()>;
fn create_user(
&self,
name: &str,
password: &str,
options: Option<CreateUserOptions>,
) -> Result<()>;
fn drop_all_users(&self, write_concern: Option<WriteConcern>) -> Result<(i32)>;
fn drop_collection(&self, name: &str) -> Result<()>;
fn drop_database(&self) -> Result<()>;
fn drop_user(&self, name: &str, Option<WriteConcern>) -> Result<()>;
fn get_all_users(&self, show_credentials: bool) -> Result<Vec<bson::Document>>;
fn get_user(&self, user: &str, options: Option<UserInfoOptions>) -> Result<bson::Document>;
fn get_users(
&self,
users: Vec<&str>,
options: Option<UserInfoOptions>,
) -> Result<Vec<bson::Document>>;
}
impl ThreadedDatabase for Database {
fn open(
client: Client,
name: &str,
read_preference: Option<ReadPreference>,
write_concern: Option<WriteConcern>,
) -> Database {
let rp = read_preference.unwrap_or_else(|| client.read_preference.to_owned());
let wc = write_concern.unwrap_or_else(|| client.write_concern.to_owned());
Arc::new(DatabaseInner {
name: String::from(name),
client: client,
read_preference: rp,
write_concern: wc,
})
}
fn auth(&self, user: &str, password: &str) -> Result<()> {
let authenticator = Authenticator::new(self.clone());
authenticator.auth(user, password)
}
fn collection(&self, coll_name: &str) -> Collection {
Collection::new(
self.clone(),
coll_name,
false,
Some(self.read_preference.to_owned()),
Some(self.write_concern.to_owned()),
)
}
fn collection_with_prefs(
&self,
coll_name: &str,
create: bool,
read_preference: Option<ReadPreference>,
write_concern: Option<WriteConcern>,
) -> Collection {
Collection::new(
self.clone(),
coll_name,
create,
read_preference,
write_concern,
)
}
fn get_req_id(&self) -> i32 {
self.client.get_req_id()
}
fn command_cursor(
&self,
spec: bson::Document,
cmd_type: CommandType,
read_pref: ReadPreference,
) -> Result<Cursor> {
Cursor::command_cursor(
self.client.clone(),
&self.name[..],
spec,
cmd_type,
read_pref,
)
}
fn command(
&self,
spec: bson::Document,
cmd_type: CommandType,
read_preference: Option<ReadPreference>,
) -> Result<bson::Document> {
let coll = self.collection("$cmd");
let options = FindOptions {
batch_size: Some(1),
read_preference: read_preference,
..FindOptions::new()
};
let res = coll.find_one_with_command_type(
Some(spec.clone()),
Some(options),
cmd_type,
)?;
res.ok_or_else(|| {
OperationError(format!("Failed to execute command with spec {:?}.", spec))
})
}
fn list_collections(&self, filter: Option<bson::Document>) -> Result<Cursor> {
self.list_collections_with_batch_size(filter, DEFAULT_BATCH_SIZE)
}
fn list_collections_with_batch_size(
&self,
filter: Option<bson::Document>,
batch_size: i32,
) -> Result<Cursor> {
let mut spec = doc!{
"listCollections": 1,
"cursor": {
"batchSize": batch_size,
},
};
if let Some(f) = filter {
spec.insert("filter", f);
}
self.command_cursor(
spec,
CommandType::ListCollections,
self.read_preference.to_owned(),
)
}
fn collection_names(&self, filter: Option<bson::Document>) -> Result<Vec<String>> {
self.list_collections(filter)?
.filter_map(|result| match result {
Err(err) => Some(Err(err)),
Ok(mut doc) => match doc.remove("name") {
Some(Bson::String(name)) => Some(Ok(name)),
_ => None,
}
})
.collect()
}
fn version(&self) -> Result<Version> {
let doc = doc! { "buildinfo": 1 };
let out = self.command(doc, CommandType::BuildInfo, None)?;
match out.get("version") {
Some(&Bson::String(ref s)) => {
match Version::parse(s) {
Ok(v) => Ok(v),
Err(e) => Err(ResponseError(String::from(e.description()))),
}
}
_ => Err(ResponseError(
String::from("No version received from server"),
)),
}
}
fn create_collection(
&self,
name: &str,
options: Option<CreateCollectionOptions>,
) -> Result<()> {
let mut doc = doc! { "create": name };
if let Some(create_collection_options) = options {
doc = merge_options(doc, create_collection_options);
}
self.command(doc, CommandType::CreateCollection, None)?;
Ok(())
}
fn create_user(
&self,
name: &str,
password: &str,
options: Option<CreateUserOptions>,
) -> Result<()> {
let mut doc = doc! {
"createUser": name,
"pwd": password
};
match options {
Some(user_options) => {
doc = merge_options(doc, user_options);
}
None => {
doc.insert("roles", Vec::new());
}
};
self.command(doc, CommandType::CreateUser, None).map(drop)
}
fn drop_all_users(&self, write_concern: Option<WriteConcern>) -> Result<(i32)> {
let mut doc = doc! { "dropAllUsersFromDatabase": 1 };
if let Some(concern) = write_concern {
doc.insert("writeConcern", concern.to_bson());
}
let response = self.command(doc, CommandType::DropAllUsers, None)?;
match response.get("n") {
Some(&Bson::I32(i)) => Ok(i),
Some(&Bson::I64(i)) => Ok(i as i32),
_ => Err(CursorNotFoundError),
}
}
fn drop_collection(&self, name: &str) -> Result<()> {
let spec = doc!{ "drop": name };
self.command(spec, CommandType::DropCollection, None).map(drop)
}
fn drop_database(&self) -> Result<()> {
let spec = doc!{ "dropDatabase": 1 };
self.command(spec, CommandType::DropDatabase, None).map(drop)
}
fn drop_user(&self, name: &str, write_concern: Option<WriteConcern>) -> Result<()> {
let mut doc = doc! { "dropUser": name };
if let Some(concern) = write_concern {
doc.insert("writeConcern", concern.to_bson());
}
self.command(doc, CommandType::DropUser, None).map(drop)
}
fn get_all_users(&self, show_credentials: bool) -> Result<Vec<bson::Document>> {
let doc = doc! {
"usersInfo": 1,
"showCredentials": show_credentials
};
let out = self.command(doc, CommandType::GetUsers, None)?;
let vec = match out.get("users") {
Some(&Bson::Array(ref vec)) => vec.clone(),
_ => return Err(CursorNotFoundError),
};
vec.into_iter()
.map(|bson| match bson {
Bson::Document(doc) => Ok(doc),
_ => Err(CursorNotFoundError),
})
.collect()
}
fn get_user(&self, user: &str, options: Option<UserInfoOptions>) -> Result<bson::Document> {
let mut doc = doc! {
"usersInfo": {
"user": user,
"db": &self.name,
},
};
if let Some(user_info_options) = options {
doc = merge_options(doc, user_info_options);
}
let out = self.command(doc, CommandType::GetUser, None)?;
let users = match out.get("users") {
Some(&Bson::Array(ref v)) => v.clone(),
_ => return Err(CursorNotFoundError),
};
match users.first() {
Some(&Bson::Document(ref doc)) => Ok(doc.clone()),
_ => Err(CursorNotFoundError),
}
}
fn get_users(
&self,
users: Vec<&str>,
options: Option<UserInfoOptions>,
) -> Result<Vec<bson::Document>> {
let vec: Vec<_> = users
.into_iter()
.map(|user| bson!({
"user": user,
"db": &self.name,
}))
.collect();
let mut doc = doc! { "usersInfo": vec };
if let Some(user_info_options) = options {
doc = merge_options(doc, user_info_options);
}
let out = self.command(doc, CommandType::GetUsers, None)?;
let vec = match out.get("users") {
Some(&Bson::Array(ref vec)) => vec.clone(),
_ => return Err(CursorNotFoundError),
};
vec.into_iter()
.map(|bson| match bson {
Bson::Document(doc) => Ok(doc),
_ => Err(CursorNotFoundError),
})
.collect()
}
}