1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::convert::TryFrom;
use std::iter::FromIterator;

use colored::Colorize;
use mongodb::bson::doc;
use mongodb::bson::Bson;
use mongodb::bson::Document;
use mongodb::options::{ClientOptions, Credential, ServerAddress, Tls, TlsOptions};
use mongodb::sync::{Client, Database};
use semver::Version;
use serde_json::{Map, Value};

use crate::consts::SUPPORTED_MONGODB_MAJOR_VERSION;
use crate::modules::types::build_info::BuildInfo;
use crate::modules::types::config::Config;

/// List all tables for given database
pub fn list_tables(config: Config) -> Option<Vec<String>> {
    let database = get_connection(config);
    let collections = database.list_collection_names(None).expect("Error!");

    if collections.is_empty() {
        return Option::from(vec![]);
    }

    return Option::from(collections);
}

/// Return table as json string
pub fn get_table(table_name: String, config: Config) -> Option<String> {
    let database = get_connection(config);
    let collection = database.collection(&table_name);

    let documents = collection.find(None, None).unwrap();

    let mut documents_vec: Vec<Document> = vec![];

    for result in documents {
        let doc: Document = result.expect("Received network error during cursor operations.");
        documents_vec.push(doc);
    }

    let documents_bson = Bson::from_iter(documents_vec);

    let data_json = serde_json::to_string_pretty(&documents_bson).unwrap();

    if data_json.is_empty() {
        return None;
    }

    return Option::from(data_json);
}

// Import collection given as json string
pub fn import_table(json: String, table_name: String, config: Config) -> () {
    let database = get_connection(config);
    let collection = database.collection(&table_name);

    // delete all documents in given collection
    collection
        .delete_many(doc! {}, None)
        .expect("Could not delete documents of collection!");

    let json_vec: Vec<Map<String, Value>> =
        serde_json::from_str(&json).expect("Could not parse json file!");

    let mut documents: Vec<Document> = vec![];

    for j in json_vec {
        let document: Document = Document::try_from(j).expect("Could not convert map to document!");
        documents.push(document);
    }

    collection
        .insert_many(documents, None)
        .expect("Error while inserting multiple collections!");
}

/// Get mongodb connection configuration
fn get_opts(config: Config) -> ClientOptions {
    let mut options = ClientOptions::default();

    let port: u16 = config
        .database
        .host_information
        .port
        .parse()
        .expect("Could not parse port!");

    let server_address: ServerAddress = ServerAddress::Tcp {
        host: config.database.host_information.address,
        port: Option::from(port),
    };

    options.hosts = vec![server_address];

    let mut credentials = Credential::default();

    credentials.username = config.database.host_information.username.into();
    credentials.source = Option::from("admin".to_string());
    credentials.password = config.database.host_information.password.into();

    options.tls = Option::from(match config.database.host_information.ssl {
        true => Tls::Enabled(TlsOptions::default()),
        false => Tls::Disabled,
    });

    return options;
}

/// Create database connection with mongodb server and return ref
fn get_connection(config: Config) -> Database {
    let options = get_opts(config.clone());
    let client = Client::with_options(options).expect("Error while connecting to mongodb server!");

    let database = client.database(config.database.database_name.as_str());

    // Get mongodb server version and check compatibility
    let build_info_doc: Document = database
        .run_command(doc! {"buildInfo": 1}, None)
        .expect("Error while getting mongodb server version!");
    let build_info: BuildInfo = bson::from_bson(Bson::Document(build_info_doc))
        .expect("Error while converting BuildInfo document to bson!");

    // Warn user if mongodb server version is greater than or equal 4.x.x
    let server_version = Version::parse(build_info.version.as_str())
        .expect("Error while parsing mongodb server version!");

    if server_version.major > SUPPORTED_MONGODB_MAJOR_VERSION {
        println!("Your mongodb server version is: {}", build_info.version);
        println!("{}", "Currently only version 3.x.x is supported!".red());
    }

    return database;
}