use awc::Client;
use awc::ClientBuilder;
use dotenv;
use std::env;
use std::path::PathBuf;
use cmd_lib::run_cmd;
use dialoguer::Confirm;
pub mod actions;
pub mod models;
pub mod responses;
use responses::all_docs::AllDocs;
use actions::db::{
all_docs::get_all_docs,
create::create_db,
db_status::get_db_status,
delete::{delete_all, delete_db},
};
use responses::db_status::DbStatus;
pub struct CouchDBClient {
host: String,
pub client: Client,
}
impl CouchDBClient {
pub fn new(host: Option<String>) -> Self {
dotenv::dotenv().ok();
let client: Client = ClientBuilder::new()
.header("User-Agent", "CouchDB-ORM")
.header("Content-Type", "application/json")
.basic_auth(
env::var("COUCHDB_ORM_NAME").expect("COUCHDB_ORM_NAME variable needed"),
Some(
&env::var("COUCHDB_ORM_PASSWORD")
.expect("COUCHDB_ORM_PASSWORD variable needed"),
),
)
.finish();
let host: String = host.unwrap_or(CouchDBClient::env_host());
CouchDBClient { client, host }
}
pub fn host(&self) -> &str {
&self.host
}
pub async fn get_db_status(
&self,
db_name: &str,
) -> Result<DbStatus, Box<dyn std::error::Error>> {
get_db_status(&self.client, db_name, &self.host).await
}
pub async fn create_db(&self, db_name: &str) -> Result<bool, Box<dyn std::error::Error>> {
create_db(&self.client, db_name, &self.host).await
}
pub async fn delete_db(&self, db_name: &str) -> Result<bool, Box<dyn std::error::Error>> {
delete_db(&self.client, db_name, &self.host).await
}
pub async fn get_all_docs(
&self,
db_name: &str,
) -> Result<AllDocs<serde_json::Value>, Box<dyn std::error::Error>> {
let mut skip: usize = 0;
let mut max_rows: usize = 1000;
let limit: usize = 100;
let mut response: AllDocs<serde_json::Value> = AllDocs {
rows: vec![],
total_rows: 0,
offset: 0,
};
let mut want_continue = true;
while skip < max_rows && want_continue {
response =
get_all_docs::<serde_json::Value>(&self.client, db_name, &self.host, skip, limit)
.await?;
max_rows = response.total_rows;
println!("{:#?}", response.rows);
if skip < max_rows {
if Confirm::new()
.with_prompt("Do you want to continue?")
.interact()?
{
skip += limit;
want_continue = true;
} else {
println!("aborting");
want_continue = false;
}
}
}
Ok(response)
}
pub async fn delete_all_docs(&self, db_name: &str) -> Result<bool, Box<dyn std::error::Error>> {
delete_all::<serde_json::Value>(&self.client, db_name, &self.host).await
}
pub async fn seed_db(
&self,
db_name: &str,
rootdir: &PathBuf,
) -> Result<bool, Box<dyn std::error::Error>> {
let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
match run_cmd! {
cd $path;
cargo run seed $db_name;
} {
Ok(()) => Ok(true),
Err(e) => Err(Box::new(e)),
}
}
pub async fn migrate_db(
&self,
db_name: &str,
rootdir: &PathBuf,
) -> Result<bool, Box<dyn std::error::Error>> {
let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
match run_cmd! {
cd $path;
cargo run migrate $db_name;
} {
Ok(()) => Ok(true),
Err(e) => Err(Box::new(e)),
}
}
pub async fn secure_db(
&self,
db_name: &str,
file: Option<String>,
rootdir: &PathBuf,
) -> Result<bool, Box<dyn std::error::Error>> {
let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
match file {
Some(f) => {
match run_cmd! {
cd $path;
cargo run secure $db_name $f;
} {
Ok(()) => Ok(true),
Err(e) => Err(Box::new(e)),
}
}
None => {
match run_cmd! {
cd $path;
cargo run secure $db_name;
} {
Ok(()) => Ok(true),
Err(e) => Err(Box::new(e)),
}
}
}
}
pub async fn backup_db(
&self,
db_name: &str,
rootdir: &PathBuf,
) -> Result<bool, Box<dyn std::error::Error>> {
let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
match run_cmd! {
cd $path;
cargo run backup $db_name;
} {
Ok(()) => Ok(true),
Err(e) => Err(Box::new(e)),
}
}
pub async fn restore_db(
&self,
db_name: &str,
rootdir: &PathBuf,
) -> Result<bool, Box<dyn std::error::Error>> {
let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
match run_cmd! {
cd $path;
cargo run restore $db_name;
} {
Ok(()) => Ok(true),
Err(e) => Err(Box::new(e)),
}
}
pub async fn create_design_doc(
&self,
db_name: &str,
rootdir: &PathBuf,
) -> Result<bool, Box<dyn std::error::Error>> {
let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
match run_cmd! {
cd $path;
cargo run design_doc_create $db_name;
} {
Ok(()) => Ok(true),
Err(e) => Err(Box::new(e)),
}
}
fn env_host() -> String {
format!(
"{}://{}:{}",
env::var("COUCHDB_PROTOCOL").expect("COUCHDB_PROTOCOL variable needed"),
env::var("COUCHDB_HOST").expect("COUCHDB_HOST variable needed"),
env::var("COUCHDB_PORT").expect("COUCHDB_PORT variable needed")
)
}
}