use std::collections::HashMap;
use reqwest::StatusCode;
use failure::Error;
use serde_json;
use serde_json::{from_reader, to_string, Value};
use client::*;
use document::*;
use error::SofaError;
use types::*;
#[derive(Debug, Clone)]
pub struct Database {
_client: Client,
name: String,
}
impl Database {
pub fn new(name: String, client: Client) -> Database {
Database {
_client: client,
name: name,
}
}
fn create_document_path(&self, id: DocumentId) -> String {
let mut result: String = self.name.clone();
result.push_str("/");
result.push_str(&id);
result
}
#[allow(dead_code)]
fn create_design_path(&self, id: DocumentId) -> String {
let mut result: String = self.name.clone();
result.push_str("/_design/");
result.push_str(&id);
result
}
fn create_compact_path(&self, design_name: &'static str) -> String {
let mut result: String = self.name.clone();
result.push_str("/_compact/");
result.push_str(design_name);
result
}
pub fn compact(&self) -> bool {
let mut path: String = self.name.clone();
path.push_str("/_compact");
let request = self._client.post(path, "".into());
request
.and_then(|mut req| {
Ok(req.send()
.and_then(|res| Ok(res.status() == StatusCode::Accepted))
.unwrap_or(false))
})
.unwrap_or(false)
}
pub fn compact_views(&self) -> bool {
let mut path: String = self.name.clone();
path.push_str("/_view_cleanup");
let request = self._client.post(path, "".into());
request
.and_then(|mut req| {
Ok(req.send()
.and_then(|res| Ok(res.status() == StatusCode::Accepted))
.unwrap_or(false))
})
.unwrap_or(false)
}
pub fn compact_index(&self, index: &'static str) -> bool {
let request = self._client.post(self.create_compact_path(index), "".into());
request
.and_then(|mut req| {
Ok(req.send()
.and_then(|res| Ok(res.status() == StatusCode::Accepted))
.unwrap_or(false))
})
.unwrap_or(false)
}
pub fn exists(&self, id: DocumentId) -> bool {
let request = self._client.head(self.create_document_path(id), None);
request
.and_then(|mut req| {
Ok(req.send()
.and_then(|res| {
Ok(match res.status() {
StatusCode::Ok | StatusCode::NotModified => true,
_ => false,
})
})
.unwrap_or(false))
})
.unwrap_or(false)
}
pub fn get(&self, id: DocumentId) -> Result<Document, Error> {
let response = self._client.get(self.create_document_path(id), None)?.send()?;
Ok(Document::new(from_reader(response)?))
}
pub fn get_bulk(&self, ids: Vec<DocumentId>) -> Result<DocumentCollection, Error> {
self.get_bulk_params(ids, None)
}
pub fn get_bulk_params(
&self,
ids: Vec<DocumentId>,
params: Option<HashMap<String, String>>,
) -> Result<DocumentCollection, Error> {
let mut options;
if let Some(opts) = params {
options = opts;
} else {
options = HashMap::new();
}
options.insert(s!("include_docs"), s!("true"));
let mut body = HashMap::new();
body.insert(s!("keys"), ids);
let response = self._client
.get(self.create_document_path("_all_docs".into()), Some(options))?
.body(to_string(&body)?)
.send()?;
Ok(DocumentCollection::new(from_reader(response)?))
}
pub fn get_all(&self) -> Result<DocumentCollection, Error> {
self.get_all_params(None)
}
pub fn get_all_params(&self, params: Option<HashMap<String, String>>) -> Result<DocumentCollection, Error> {
let mut options;
if let Some(opts) = params {
options = opts;
} else {
options = HashMap::new();
}
options.insert(s!("include_docs"), s!("true"));
let response = self._client
.get(self.create_document_path("_all_docs".into()), Some(options))?
.send()?;
Ok(DocumentCollection::new(from_reader(response)?))
}
pub fn find(&self, params: Value) -> Result<DocumentCollection, Error> {
let path = self.create_document_path("_find".into());
let response = self._client.post(path, js!(¶ms))?.send()?;
let data: FindResult = from_reader(response)?;
if let Some(doc_val) = data.docs {
let documents: Vec<Document> = doc_val
.into_iter()
.filter(|d| {
let id: String = json_extr!(d["_id"]);
!id.starts_with('_')
})
.map(|v| Document::new(v.clone()))
.collect();
Ok(DocumentCollection::new_from_documents(documents))
} else if let Some(err) = data.error {
Err(SofaError(err).into())
} else {
Ok(DocumentCollection::default())
}
}
pub fn save(&self, doc: Document) -> Result<Document, Error> {
let id = doc._id.to_owned();
let raw = doc.get_data();
let response = self._client
.put(self.create_document_path(id), to_string(&raw)?)?
.send()?;
let data: DocumentCreatedResult = from_reader(response)?;
match data.ok {
Some(true) => {
let mut val = doc.get_data();
val["_rev"] = json!(data.rev);
Ok(Document::new(val))
}
Some(false) | _ => {
let err = data.error.unwrap_or(s!("unspecified error"));
return Err(SofaError(err).into());
}
}
}
pub fn create(&self, raw_doc: Value) -> Result<Document, Error> {
let response = self._client.post(self.name.clone(), to_string(&raw_doc)?)?.send()?;
let data: DocumentCreatedResult = from_reader(response)?;
match data.ok {
Some(true) => {
let data_id = match data.id {
Some(id) => id,
_ => return Err(SofaError(s!("invalid id")).into()),
};
let data_rev = match data.rev {
Some(rev) => rev,
_ => return Err(SofaError(s!("invalid rev")).into()),
};
let mut val = raw_doc.clone();
val["_id"] = json!(data_id);
val["_rev"] = json!(data_rev);
Ok(Document::new(val))
}
Some(false) | _ => {
let err = data.error.unwrap_or(s!("unspecified error"));
return Err(SofaError(err).into());
}
}
}
pub fn remove(&self, doc: Document) -> bool {
let request = self._client.delete(
self.create_document_path(doc._id.clone()),
Some({
let mut h = HashMap::new();
h.insert(s!("rev"), doc._rev.clone());
h
}),
);
request
.and_then(|mut req| {
Ok(req.send()
.and_then(|res| {
Ok(match res.status() {
StatusCode::Ok | StatusCode::Accepted => true,
_ => false,
})
})
.unwrap_or(false))
})
.unwrap_or(false)
}
pub fn insert_index(&self, name: String, spec: IndexFields) -> Result<IndexCreated, Error> {
let response = self._client
.post(
self.create_document_path("_index".into()),
js!(json!({
"name": name,
"index": spec
})),
)?
.send()?;
let data: IndexCreated = from_reader(response)?;
if data.error.is_some() {
let err = data.error.unwrap_or(s!("unspecified error"));
Err(SofaError(err).into())
} else {
Ok(data)
}
}
pub fn read_indexes(&self) -> Result<DatabaseIndexList, Error> {
let response = self._client
.get(self.create_document_path("_index".into()), None)?
.send()?;
Ok(from_reader(response)?)
}
pub fn ensure_index(&self, name: String, spec: IndexFields) -> Result<bool, Error> {
let db_indexes = self.read_indexes()?;
for i in db_indexes.indexes.into_iter() {
if i.name == name {
return Ok(false);
}
}
let _ = self.insert_index(name, spec)?;
Ok(true)
}
}