xxpg/
lib.rs

1pub use async_lazy;
2use async_lazy::Lazy;
3pub use ctor::ctor;
4pub use paste::paste;
5pub use tokio_postgres::{self, types::ToSql, Client, Error, NoTls, Row, Statement, ToStatement};
6use tokio_postgres::{error::SqlState, ToStatementType};
7use tracing::error;
8pub use trt::TRT;
9pub use xxpg_proc::{Q, Q01, Q1};
10
11pub struct LazyStatement {
12  pub statement: async_lazy::Lazy<tokio_postgres::Statement>,
13  pub sql: &'static str,
14}
15
16impl ToStatement for LazyStatement {
17  fn __convert(&self) -> ToStatementType<'_> {
18    ToStatementType::Statement(self.statement.get().unwrap())
19  }
20}
21
22#[macro_export]
23macro_rules! sql {
24    ($($var:ident : $sql:expr),+ ) => {
25        $(
26            $crate::paste!{
27                pub static [<__ $var:upper >]: $crate::LazyStatement  =
28                    $crate::LazyStatement{
29                        statement:$crate::async_lazy::Lazy::const_new(|| Box::pin(async move { $crate::PG.force().await.prepare($sql).await.unwrap() })),
30                        sql:$sql
31                    };
32                pub static [<$var:upper>] : &$crate::LazyStatement  = &[<__ $var:upper>];
33            }
34        )+
35
36            mod private {
37                #[$crate::ctor]
38                fn pg_statement_init() {
39                    $crate::TRT.block_on(async move {
40                        $crate::paste!{
41                            $(super::[<$var:upper>].statement.force().await;)+
42                        }
43                    });
44                }
45            }
46    };
47}
48
49//   r = ONE0"SELECT name FROM img.sampler WHERE id=#{id}"
50// else
51//   r = await LI"SELECT id,name FROM img.sampler"
52
53pub async fn conn() -> Client {
54  let pg_uri = std::env::var("PG_URI").unwrap();
55  let (client, connection) = tokio_postgres::connect(&format!("postgres://{}", pg_uri), NoTls)
56    .await
57    .unwrap();
58
59  tokio::spawn(async move {
60    if let Err(e) = connection.await {
61      let err_code = e.code();
62      let code = match err_code {
63        Some(code) => code.code(),
64        None => "",
65      };
66      tracing::error!("❌ POSTGRES ERROR CODE {code} : {e}\n SEE https://www.postgresql.org/docs/current/errcodes-appendix.html\n");
67
68      if err_code == Some(&SqlState::ADMIN_SHUTDOWN) || e.is_closed() {
69        std::process::exit(1)
70      }
71    }
72  });
73
74  client
75}
76
77pub static PG: Lazy<Client> = Lazy::const_new(|| Box::pin(async move { conn().await }));
78
79#[ctor]
80fn init() {
81  TRT.block_on(async move {
82    use std::future::IntoFuture;
83    PG.into_future().await;
84  });
85}
86
87macro_rules! q {
88  ($name:ident,$func:ident,$rt:ty) => {
89    #[allow(non_snake_case)]
90    pub async fn $name<T>(statement: &T, params: &[&(dyn ToSql + Sync)]) -> Result<$rt, Error>
91    where
92      T: ?Sized + ToStatement,
93    {
94      match PG.get().unwrap().$func(statement, params).await {
95        Ok(r) => Ok(r),
96        Err(err) => {
97          if err.is_closed() {
98            error!("{}", err);
99            std::process::exit(1);
100          }
101          Err(err)
102        }
103      }
104    }
105  };
106}
107
108q!(Q, query, Vec<Row>);
109q!(Q1, query_one, Row);
110q!(Q01, query_opt, Option<Row>);