Skip to main content

welds_connections/
lib.rs

1pub use crate::errors::Error;
2use crate::errors::Result;
3use async_trait::async_trait;
4pub use row::Row;
5pub use transaction::Transaction;
6pub mod any;
7pub mod errors;
8#[cfg(feature = "mssql")]
9pub mod mssql;
10#[cfg(feature = "mysql")]
11pub mod mysql;
12#[cfg(feature = "noop")]
13pub mod noop;
14#[cfg(feature = "postgres")]
15pub mod postgres;
16pub mod row;
17#[cfg(feature = "sqlite")]
18pub mod sqlite;
19#[cfg(feature = "sqlite-sync")]
20pub mod sqlite_sync;
21pub mod trace;
22pub mod transaction;
23
24#[cfg(feature = "unstable-api")]
25use futures_core::stream::BoxStream;
26
27pub struct Fetch<'s, 'args, 't> {
28    pub sql: &'s str,
29    pub params: &'args [&'t (dyn Param + Sync)],
30}
31
32#[maybe_async::async_impl]
33pub trait AsyncNeedsSyncSend: Sync + Send {}
34#[maybe_async::async_impl]
35impl<T: Sync + Send> AsyncNeedsSyncSend for T {}
36
37#[maybe_async::sync_impl]
38pub trait AsyncNeedsSyncSend {}
39#[maybe_async::sync_impl]
40impl<T> AsyncNeedsSyncSend for T {}
41
42#[maybe_async::maybe_async]
43#[async_trait]
44/// The common trait for database connections and transactions.
45pub trait Client: AsyncNeedsSyncSend {
46    /// Execute a sql command. returns the number of rows that were affected
47    async fn execute(&self, sql: &str, params: &[&(dyn Param + Sync)]) -> Result<ExecuteResult>;
48
49    /// Runs SQL and returns a collection of rows from the database.
50    async fn fetch_rows(&self, sql: &str, params: &[&(dyn Param + Sync)]) -> Result<Vec<Row>>;
51
52    /// Run several `fetch_rows` command on the same connection in the connection pool
53    async fn fetch_many<'s, 'args, 't>(
54        &self,
55        args: &[Fetch<'s, 'args, 't>],
56    ) -> Result<Vec<Vec<Row>>>;
57
58    // Returns what syntax (dialect) of SQL the backend is expecting
59    fn syntax(&self) -> Syntax;
60}
61
62#[cfg(feature = "unstable-api")]
63#[async_trait]
64/// The common trait for database connections and transactions.
65pub trait StreamClient: Sync + Send {
66    /// Run the SQL streaming the results back in a future::stream
67    async fn stream<'client, 'e, 'params>(
68        &'client self,
69        sql: &str,
70        params: &[&'params (dyn Param + Sync)],
71    ) -> BoxStream<'e, Result<Row>>
72    where
73        'client: 'e,
74        'params: 'e;
75}
76
77/// Used the ENV DATABASE_URL
78/// builds a connection with whatever is in it.
79#[maybe_async::maybe_async]
80pub async fn connect_from_env() -> Result<any::AnyClient> {
81    let url = std::env::var("DATABASE_URL").or(Err(Error::InvalidDatabaseUrl))?;
82    connect(&url).await
83}
84
85/// Returns a connection pool (Client/TransactStart) for the given connection string.
86///
87/// To use, make sure your database feature is enabled
88///
89/// connection string formats:
90/// SQLX Connection String (postgres, mysql, sqlite)
91/// ADO Connection String (mssql)
92#[maybe_async::maybe_async]
93pub async fn connect(cs: impl Into<String>) -> Result<any::AnyClient> {
94    let cs: String = cs.into();
95    #[cfg(feature = "postgres")]
96    if cs.starts_with("postgresql:") {
97        log::debug!("Welds connecting to Postgres");
98        let client = postgres::connect(&cs).await?;
99        return Ok(any::AnyClient::Postgres(client));
100    }
101    #[cfg(feature = "postgres")]
102    if cs.starts_with("postgres:") {
103        log::debug!("Welds connecting to Postgres");
104        let client = postgres::connect(&cs).await?;
105        return Ok(any::AnyClient::Postgres(client));
106    }
107    #[cfg(feature = "mysql")]
108    if cs.starts_with("mysql:") {
109        log::debug!("Welds connecting to MySql");
110        let client = mysql::connect(&cs).await?;
111        return Ok(any::AnyClient::Mysql(client));
112    }
113    #[cfg(feature = "sqlite")]
114    if cs.starts_with("sqlite:") {
115        log::debug!("Welds connecting to Sqlite");
116        let client = sqlite::connect(&cs).await?;
117        return Ok(any::AnyClient::Sqlite(client));
118    }
119    #[cfg(feature = "sqlite-sync")]
120    if cs.starts_with("sqlite:") {
121        log::debug!("Welds connecting to Sqlite (Sync)");
122        let client = sqlite_sync::connect(&cs)?;
123        return Ok(any::AnyClient::SqliteSync(client));
124    }
125    #[cfg(feature = "mssql")]
126    if !cs.is_empty() {
127        log::debug!("Welds connecting to MSSQL");
128        let client = mssql::connect(&cs).await?;
129        return Ok(any::AnyClient::Mssql(client));
130    }
131    log::error!(
132        "Database backend unknown for given connection string. Did you enable the backend feature for this connection type in welds?"
133    );
134    Err(errors::Error::InvalidDatabaseUrl)
135}
136
137#[maybe_async::maybe_async]
138#[async_trait]
139/// Implementers of this trait can crate a transaction.
140/// If you want to create a transaction off of a Client,
141/// make sure you `use welds::TransactStart`
142pub trait TransactStart {
143    async fn begin<'t>(&'t self) -> Result<Transaction<'t>>;
144}
145
146// This code is scripted out cuz writing it for all the features to be to much
147mod params;
148pub use params::Param;
149
150pub struct ExecuteResult {
151    pub(crate) rows_affected: u64,
152}
153
154impl ExecuteResult {
155    pub fn new(rows_affected: u64) -> Self {
156        Self { rows_affected }
157    }
158
159    pub fn rows_affected(&self) -> u64 {
160        self.rows_affected
161    }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
165pub enum Syntax {
166    Mysql,
167    Postgres,
168    Sqlite,
169    Mssql,
170}