1
#[cfg(feature = "mssql")]
2
pub extern crate async_std;
3
pub extern crate futures;
4
pub extern crate lazy_static;
5
#[cfg(feature = "mysql")]
6
pub extern crate mysql_async;
7
#[cfg(feature = "mssql")]
8
pub extern crate tiberius;
9
pub extern crate tokio;
10
#[cfg(feature = "postgres")]
11
pub extern crate tokio_postgres;
12
pub extern crate tokio_util;
13

            
14
pub mod canyon_database_connector;
15
pub mod datasources;
16

            
17
use std::fs;
18
use std::path::PathBuf;
19

            
20
use crate::datasources::{CanyonSqlConfig, DatasourceConfig};
21
use canyon_database_connector::DatabaseConnection;
22
use indexmap::IndexMap;
23
use lazy_static::lazy_static;
24
use tokio::sync::{Mutex, MutexGuard};
25
use walkdir::WalkDir;
26

            
27
lazy_static! {
28
    pub static ref CANYON_TOKIO_RUNTIME: tokio::runtime::Runtime =
29
        tokio::runtime::Runtime::new()  // TODO Make the config with the builder
30
            .expect("Failed initializing the Canyon-SQL Tokio Runtime");
31

            
32
    static ref RAW_CONFIG_FILE: String = fs::read_to_string(find_canyon_config_file())
33
        .expect("Error opening or reading the Canyon configuration file");
34
    static ref CONFIG_FILE: CanyonSqlConfig = toml::from_str(RAW_CONFIG_FILE.as_str())
35
        .expect("Error generating the configuration for Canyon-SQL");
36

            
37
    pub static ref DATASOURCES: Vec<DatasourceConfig> =
38
        CONFIG_FILE.canyon_sql.datasources.clone();
39

            
40
    pub static ref CACHED_DATABASE_CONN: Mutex<IndexMap<&'static str, DatabaseConnection>> =
41
        Mutex::new(IndexMap::new());
42
}
43

            
44
56
fn find_canyon_config_file() -> PathBuf {
45
3652
    for e in WalkDir::new(".")
46
        .max_depth(2)
47
        .into_iter()
48
3596
        .filter_map(|e| e.ok())
49
    {
50
3596
        let filename = e.file_name().to_str().unwrap();
51
3596
        if e.metadata().unwrap().is_file()
52
1667
            && filename.starts_with("canyon")
53
112
            && filename.ends_with(".toml")
54
        {
55
56
            return e.path().to_path_buf();
56
        }
57
3596
    }
58

            
59
    panic!()
60
56
}
61

            
62
/// Convenient free function to initialize a kind of connection pool based on the datasources present defined
63
/// in the configuration file.
64
///
65
/// This avoids Canyon to create a new connection to the database on every query, potentially avoiding bottlenecks
66
/// coming from the instantiation of that new conn every time.
67
///
68
/// Note: We noticed with the integration tests that the [`tokio_postgres`] crate (PostgreSQL) is able to work in an async environment
69
/// with a new connection per query without no problem, but the [`tiberius`] crate (MSSQL) suffers a lot when it has continuous
70
/// statements with multiple queries, like and insert followed by a find by id to check if the insert query has done its
71
/// job done.
72
734
pub async fn init_connections_cache() {
73
224
    for datasource in DATASOURCES.iter() {
74
336
        CACHED_DATABASE_CONN.lock().await.insert(
75
168
            &datasource.name,
76
672
            DatabaseConnection::new(datasource)
77
958
                .await
78
168
                .unwrap_or_else(|_| {
79
                    panic!(
80
                        "Error pooling a new connection for the datasource: {:?}",
81
                        datasource.name
82
                    )
83
                }),
84
168
        );
85
    }
86
112
}
87

            
88
///
89
92
pub fn get_database_connection<'a>(
90
    datasource_name: &str,
91
    guarded_cache: &'a mut MutexGuard<IndexMap<&str, DatabaseConnection>>,
92
) -> &'a mut DatabaseConnection {
93
92
    if datasource_name.is_empty() {
94
60
        guarded_cache
95
            .get_mut(
96
30
                DATASOURCES
97
                    .get(0)
98
                    .expect("We didn't found any valid datasource configuration. Check your `canyon.toml` file")
99
                    .name
100
                    .as_str()
101
            ).unwrap_or_else(|| panic!("No default datasource found. Check your `canyon.toml` file"))
102
    } else {
103
124
        guarded_cache.get_mut(datasource_name)
104
62
            .unwrap_or_else(||
105
                panic!("Canyon couldn't find a datasource in the pool with the argument provided: {datasource_name}")
106
            )
107
    }
108
92
}
109

            
110
30
pub fn get_database_config<'a>(
111
    datasource_name: &str,
112
    datasources_config: &'a [DatasourceConfig],
113
) -> &'a DatasourceConfig {
114
30
    if datasource_name.is_empty() {
115
15
        datasources_config
116
            .get(0)
117
            .unwrap_or_else(|| panic!("Not exist datasource"))
118
    } else {
119
45
        datasources_config
120
            .iter()
121
53
            .find(|dc| dc.name == datasource_name)
122
15
            .unwrap_or_else(|| panic!("Not found datasource expected {datasource_name}"))
123
    }
124
30
}