endpoint_libs/libs/
database.rs

1use deadpool_postgres::*;
2use eyre::*;
3use postgres_from_row::FromRow;
4use secrecy::SecretString;
5use serde::de::DeserializeOwned;
6use serde::{Deserialize, Serialize};
7use std::fmt::Debug;
8use std::path::Path;
9use std::process::Command;
10use std::time::Duration;
11pub use tokio_postgres::types::ToSql;
12pub use tokio_postgres::Row;
13mod data_thread;
14mod pooled;
15pub use data_thread::*;
16pub use pooled::*;
17
18use super::datatable::RDataTable;
19
20#[derive(Clone, Debug, Default, Deserialize)]
21pub struct DatabaseConfig {
22    /// See [`tokio_postgres::Config::user`].
23    pub user: Option<String>,
24    /// See [`tokio_postgres::Config::password`].
25    pub password: Option<SecretString>,
26    /// See [`tokio_postgres::Config::dbname`].
27    pub dbname: Option<String>,
28    /// See [`tokio_postgres::Config::options`].
29    pub options: Option<String>,
30    /// See [`tokio_postgres::Config::application_name`].
31    pub application_name: Option<String>,
32    /// See [`tokio_postgres::Config::ssl_mode`].
33    pub ssl_mode: Option<SslMode>,
34    /// This is similar to [`Config::hosts`] but only allows one host to be
35    /// specified.
36    ///
37    /// Unlike [`tokio_postgres::Config`] this structure differentiates between
38    /// one host and more than one host. This makes it possible to store this
39    /// configuration in an environment variable.
40    ///
41    /// See [`tokio_postgres::Config::host`].
42    pub host: Option<String>,
43    /// See [`tokio_postgres::Config::host`].
44    pub hosts: Option<Vec<String>>,
45    /// This is similar to [`Config::ports`] but only allows one port to be
46    /// specified.
47    ///
48    /// Unlike [`tokio_postgres::Config`] this structure differentiates between
49    /// one port and more than one port. This makes it possible to store this
50    /// configuration in an environment variable.
51    ///
52    /// See [`tokio_postgres::Config::port`].
53    pub port: Option<u16>,
54    /// See [`tokio_postgres::Config::port`].
55    pub ports: Option<Vec<u16>>,
56    /// See [`tokio_postgres::Config::connect_timeout`].
57    pub connect_timeout: Option<Duration>,
58    /// See [`tokio_postgres::Config::keepalives`].
59    pub keepalives: Option<bool>,
60    /// See [`tokio_postgres::Config::keepalives_idle`].
61    pub keepalives_idle: Option<Duration>,
62    /// See [`tokio_postgres::Config::target_session_attrs`].
63    pub target_session_attrs: Option<TargetSessionAttrs>,
64    /// See [`tokio_postgres::Config::channel_binding`].
65    pub channel_binding: Option<ChannelBinding>,
66
67    /// [`Manager`] configuration.
68    ///
69    /// [`Manager`]: super::Manager
70    pub manager: Option<ManagerConfig>,
71
72    /// [`Pool`] configuration.
73    pub pool: Option<PoolConfig>,
74}
75
76pub trait DatabaseRequest: Send {
77    type ResponseRow: Send + Sync + Clone + Serialize + DeserializeOwned + FromRow;
78    fn statement(&self) -> &str;
79    fn params(&self) -> Vec<&(dyn ToSql + Sync)>;
80}
81pub type DatabaseRequestBoxed = Box<dyn DatabaseRequest<ResponseRow = Row>>;
82#[derive(Clone)]
83pub enum DbClient {
84    Pooled(PooledDbClient),
85    Threaded(ThreadedDbClient),
86}
87impl From<PooledDbClient> for DbClient {
88    fn from(client: PooledDbClient) -> Self {
89        Self::Pooled(client)
90    }
91}
92impl From<ThreadedDbClient> for DbClient {
93    fn from(value: ThreadedDbClient) -> Self {
94        Self::Threaded(value)
95    }
96}
97impl DbClient {
98    pub async fn execute<T>(&self, req: T) -> Result<RDataTable<T::ResponseRow>>
99    where
100        T: DatabaseRequest + Sync + Send + Debug + 'static,
101        T::ResponseRow: FromRow + Sync + Send + Clone + Debug + Sized + 'static,
102    {
103        match self {
104            DbClient::Pooled(client) => client.execute(req).await,
105            DbClient::Threaded(client) => client.execute(req).await,
106        }
107    }
108}
109
110pub fn database_test_config() -> DatabaseConfig {
111    DatabaseConfig {
112        user: Some("postgres".to_string()),
113        password: Some("123456".to_string().into()),
114        dbname: Some("red_alert".to_string()),
115        host: Some("localhost".to_string()),
116        ..Default::default()
117    }
118}
119
120pub fn drop_and_recreate_database() -> Result<()> {
121    let script = Path::new("scripts/drop_and_recreate_database.sh");
122    Command::new("bash")
123        .arg(script)
124        .arg("etc/config.json")
125        .status()?;
126    Ok(())
127}