1
//! Traits and implementations for accessing databases and caches.
2
//!
3
//! Currently we only provide pure MongoDB/SQLite implementation. Mixing implementation is
4
//! possible. For example, put units/devices in MongoDB and put routes in Redis. Then use a
5
//! model struct and impl to mix both databases.
6

            
7
use std::{error::Error as StdError, sync::Arc};
8

            
9
use async_trait::async_trait;
10

            
11
pub mod application;
12
pub mod device;
13
pub mod device_route;
14
pub mod dldata_buffer;
15
pub mod network;
16
pub mod network_route;
17
pub mod unit;
18

            
19
mod cache_memory;
20
mod memory;
21
mod model_mongodb;
22
mod model_sqlite;
23
mod mongodb;
24
mod sqlite;
25

            
26
pub use self::{
27
    cache_memory::{Cache as MemoryCache, Options as MemoryOptions},
28
    memory::{
29
        device::Options as DeviceOptions, device_route::Options as DeviceRouteOptions,
30
        network_route::Options as NetworkRouteOptions,
31
    },
32
    mongodb::conn::{self as mongodb_conn, Options as MongoDbOptions},
33
    sqlite::conn::{self as sqlite_conn, Options as SqliteOptions},
34
};
35
pub use model_mongodb::Model as MongoDbModel;
36
pub use model_sqlite::Model as SqliteModel;
37

            
38
/// Database connection options for model implementation.
39
pub enum ConnOptions {
40
    // Pure MongoDB model implementation.
41
    MongoDB(MongoDbOptions),
42
    /// Pure SQLite model implementation.
43
    Sqlite(SqliteOptions),
44
}
45

            
46
/// Database connection options for cache implementation.
47
pub enum CacheConnOptions {
48
    Memory {
49
        device: DeviceOptions,
50
        device_route: DeviceRouteOptions,
51
        network_route: NetworkRouteOptions,
52
    },
53
}
54

            
55
/// The top level trait to get all models (tables/collections).
56
#[async_trait]
57
pub trait Model: Send + Sync {
58
    /// Close database connection.
59
    async fn close(&self) -> Result<(), Box<dyn StdError>>;
60

            
61
    /// To get the unit model.
62
    fn unit(&self) -> &dyn unit::UnitModel;
63

            
64
    /// To get the application model.
65
    fn application(&self) -> &dyn application::ApplicationModel;
66

            
67
    /// To get the network model.
68
    fn network(&self) -> &dyn network::NetworkModel;
69

            
70
    /// To get the device model.
71
    fn device(&self) -> &dyn device::DeviceModel;
72

            
73
    /// To get the device route model.
74
    fn device_route(&self) -> &dyn device_route::DeviceRouteModel;
75

            
76
    /// To get the network route model.
77
    fn network_route(&self) -> &dyn network_route::NetworkRouteModel;
78

            
79
    /// To get the downlink data buffer model.
80
    fn dldata_buffer(&self) -> &dyn dldata_buffer::DlDataBufferModel;
81
}
82

            
83
/// The top level trait to get all caches.
84
#[async_trait]
85
pub trait Cache: Send + Sync {
86
    /// Close database connection.
87
    async fn close(&self) -> Result<(), Box<dyn StdError>>;
88

            
89
    /// To get the device cache.
90
    fn device(&self) -> &dyn device::DeviceCache;
91

            
92
    /// To get the device route cache.
93
    fn device_route(&self) -> &dyn device_route::DeviceRouteCache;
94

            
95
    /// To get the network route cache.
96
    fn network_route(&self) -> &dyn network_route::NetworkRouteCache;
97
}
98

            
99
/// To create the database model with the specified database implementation.
100
25
pub async fn new(opts: &ConnOptions) -> Result<Arc<dyn Model>, Box<dyn StdError>> {
101
25
    let model: Arc<dyn Model> = match opts {
102
6
        ConnOptions::MongoDB(opts) => Arc::new(MongoDbModel::new(opts).await?),
103
19
        ConnOptions::Sqlite(opts) => Arc::new(SqliteModel::new(opts).await?),
104
    };
105
25
    model.unit().init().await?;
106
25
    model.application().init().await?;
107
25
    model.network().init().await?;
108
25
    model.device().init().await?;
109
25
    model.device_route().init().await?;
110
25
    model.network_route().init().await?;
111
25
    model.dldata_buffer().init().await?;
112
25
    Ok(model)
113
25
}
114

            
115
/// To create the database cache with the specified database implementation.
116
10
pub async fn new_cache(
117
10
    opts: &CacheConnOptions,
118
10
    model: &Arc<dyn Model>,
119
10
) -> Result<Arc<dyn Cache>, Box<dyn StdError>> {
120
10
    let cache: Arc<dyn Cache> = match opts {
121
10
        CacheConnOptions::Memory {
122
10
            device,
123
10
            device_route,
124
10
            network_route,
125
10
        } => {
126
10
            let opts = MemoryOptions {
127
10
                device: &device,
128
10
                device_route: &device_route,
129
10
                network_route: &network_route,
130
10
            };
131
10
            Arc::new(MemoryCache::new(&opts, model))
132
10
        }
133
10
    };
134
10
    Ok(cache)
135
10
}