couchdb-orm 0.1.6

couchdb-orm Copyright (C) 2020-2023 OpenToolAdd This program comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under certain conditions; type `show-license' for details. A CLI ORM to manage some Databases operations like migration, schema creation, etc.... For the moment it only handle CouchDB.
Documentation
// Copyright (C) 2020-2023  OpenToolAdd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
// contact: contact@tool-add.com

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;
        // println!("max_rows: {}", max_rows);
        while skip < max_rows && want_continue {
            // println!("chunk {} migrated", skip / 100);
            // println!("max rows {}", max_rows);
            // println!("skip {}", skip);
            // println!("limit {}", limit);
            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")
        )
    }
}