couchdb_orm/client/couchdb/
mod.rs

1// Copyright (C) 2020-2023  OpenToolAdd
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program.  If not, see <http://www.gnu.org/licenses/>.
15// contact: contact@tool-add.com
16
17use awc::Client;
18use awc::ClientBuilder;
19use dotenv;
20use std::env;
21use std::path::PathBuf;
22
23use cmd_lib::run_cmd;
24use dialoguer::Confirm;
25
26pub mod actions;
27pub mod models;
28pub mod responses;
29
30use responses::all_docs::AllDocs;
31
32use actions::db::{
33    all_docs::get_all_docs,
34    create::create_db,
35    db_status::get_db_status,
36    delete::{delete_all, delete_db},
37};
38
39use responses::db_status::DbStatus;
40
41pub struct CouchDBClient {
42    host: String,
43    pub client: Client,
44}
45
46impl CouchDBClient {
47    pub fn new(host: Option<String>) -> Self {
48        dotenv::dotenv().ok();
49        let client: Client = ClientBuilder::new()
50            .header("User-Agent", "CouchDB-ORM")
51            .header("Content-Type", "application/json")
52            .basic_auth(
53                env::var("COUCHDB_ORM_NAME").expect("COUCHDB_ORM_NAME variable needed"),
54                Some(
55                    &env::var("COUCHDB_ORM_PASSWORD")
56                        .expect("COUCHDB_ORM_PASSWORD variable needed"),
57                ),
58            )
59            .finish();
60        let host: String = host.unwrap_or(CouchDBClient::env_host());
61        CouchDBClient { client, host }
62    }
63
64    pub fn host(&self) -> &str {
65        &self.host
66    }
67
68    pub async fn get_db_status(
69        &self,
70        db_name: &str,
71    ) -> Result<DbStatus, Box<dyn std::error::Error>> {
72        get_db_status(&self.client, db_name, &self.host).await
73    }
74
75    pub async fn create_db(&self, db_name: &str) -> Result<bool, Box<dyn std::error::Error>> {
76        create_db(&self.client, db_name, &self.host).await
77    }
78
79    pub async fn delete_db(&self, db_name: &str) -> Result<bool, Box<dyn std::error::Error>> {
80        delete_db(&self.client, db_name, &self.host).await
81    }
82
83    pub async fn get_all_docs(
84        &self,
85        db_name: &str,
86    ) -> Result<AllDocs<serde_json::Value>, Box<dyn std::error::Error>> {
87        let mut skip: usize = 0;
88        let mut max_rows: usize = 1000;
89        let limit: usize = 100;
90        let mut response: AllDocs<serde_json::Value> = AllDocs {
91            rows: vec![],
92            total_rows: 0,
93            offset: 0,
94        };
95        let mut want_continue = true;
96        // println!("max_rows: {}", max_rows);
97        while skip < max_rows && want_continue {
98            // println!("chunk {} migrated", skip / 100);
99            // println!("max rows {}", max_rows);
100            // println!("skip {}", skip);
101            // println!("limit {}", limit);
102            response =
103                get_all_docs::<serde_json::Value>(&self.client, db_name, &self.host, skip, limit)
104                    .await?;
105            max_rows = response.total_rows;
106            println!("{:#?}", response.rows);
107
108            if skip < max_rows {
109                if Confirm::new()
110                    .with_prompt("Do you want to continue?")
111                    .interact()?
112                {
113                    skip += limit;
114                    want_continue = true;
115                } else {
116                    println!("aborting");
117                    want_continue = false;
118                }
119            }
120        }
121        Ok(response)
122    }
123
124    pub async fn delete_all_docs(&self, db_name: &str) -> Result<bool, Box<dyn std::error::Error>> {
125        delete_all::<serde_json::Value>(&self.client, db_name, &self.host).await
126    }
127
128    pub async fn seed_db(
129        &self,
130        db_name: &str,
131        rootdir: &PathBuf,
132    ) -> Result<bool, Box<dyn std::error::Error>> {
133        let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
134        match run_cmd! {
135            cd $path;
136            cargo run seed $db_name;
137        } {
138            Ok(()) => Ok(true),
139            Err(e) => Err(Box::new(e)),
140        }
141    }
142
143    pub async fn migrate_db(
144        &self,
145        db_name: &str,
146        rootdir: &PathBuf,
147    ) -> Result<bool, Box<dyn std::error::Error>> {
148        let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
149        match run_cmd! {
150            cd $path;
151            cargo run migrate $db_name;
152        } {
153            Ok(()) => Ok(true),
154            Err(e) => Err(Box::new(e)),
155        }
156    }
157
158    pub async fn secure_db(
159        &self,
160        db_name: &str,
161        file: Option<String>,
162        rootdir: &PathBuf,
163    ) -> Result<bool, Box<dyn std::error::Error>> {
164        let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
165        match file {
166            Some(f) => {
167                match run_cmd! {
168                    cd $path;
169                    cargo run secure $db_name $f;
170                } {
171                    Ok(()) => Ok(true),
172                    Err(e) => Err(Box::new(e)),
173                }
174            }
175            None => {
176                match run_cmd! {
177                    cd $path;
178                    cargo run secure $db_name;
179                } {
180                    Ok(()) => Ok(true),
181                    Err(e) => Err(Box::new(e)),
182                }
183            }
184        }
185    }
186
187    pub async fn backup_db(
188        &self,
189        db_name: &str,
190        rootdir: &PathBuf,
191    ) -> Result<bool, Box<dyn std::error::Error>> {
192        let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
193        match run_cmd! {
194            cd $path;
195            cargo run backup $db_name;
196        } {
197            Ok(()) => Ok(true),
198            Err(e) => Err(Box::new(e)),
199        }
200    }
201
202    pub async fn restore_db(
203        &self,
204        db_name: &str,
205        rootdir: &PathBuf,
206    ) -> Result<bool, Box<dyn std::error::Error>> {
207        let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
208        match run_cmd! {
209            cd $path;
210            cargo run restore $db_name;
211        } {
212            Ok(()) => Ok(true),
213            Err(e) => Err(Box::new(e)),
214        }
215    }
216
217    pub async fn create_design_doc(
218        &self,
219        db_name: &str,
220        rootdir: &PathBuf,
221    ) -> Result<bool, Box<dyn std::error::Error>> {
222        let path: String = format!("{}/_meta", rootdir.to_str().unwrap());
223        match run_cmd! {
224            cd $path;
225            cargo run design_doc_create $db_name;
226        } {
227            Ok(()) => Ok(true),
228            Err(e) => Err(Box::new(e)),
229        }
230    }
231
232    fn env_host() -> String {
233        format!(
234            "{}://{}:{}",
235            env::var("COUCHDB_PROTOCOL").expect("COUCHDB_PROTOCOL variable needed"),
236            env::var("COUCHDB_HOST").expect("COUCHDB_HOST variable needed"),
237            env::var("COUCHDB_PORT").expect("COUCHDB_PORT variable needed")
238        )
239    }
240}