Skip to content

Commit

Permalink
0.31: move sync API to SyncClient
Browse files Browse the repository at this point in the history
  • Loading branch information
psarna committed Jun 26, 2023
1 parent 8745398 commit 9f4ec9b
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 80 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libsql-client"
version = "0.30.2"
version = "0.31.0"
edition = "2021"
license = "Apache-2.0"
description = "HTTP-based client for libSQL and sqld"
Expand Down
163 changes: 98 additions & 65 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! `Client` is the main structure to interact with the database.
use anyhow::Result;

use crate::{proto, BatchResult, ResultSet, Statement, Transaction};
use crate::{proto, BatchResult, ResultSet, Statement, SyncTransaction, Transaction};

static TRANSACTION_IDS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);

Expand All @@ -22,6 +22,12 @@ pub enum Client {
Hrana(crate::hrana::Client),
}

/// A synchronous flavor of `Client`. All its public methods are synchronous,
/// to make it usable in environments that don't support async/await.
pub struct SyncClient {
inner: Client,
}

unsafe impl Send for Client {}

impl Client {
Expand Down Expand Up @@ -112,10 +118,6 @@ impl Client {
}
}

pub fn execute_sync(&self, stmt: impl Into<Statement> + Send) -> Result<ResultSet> {
futures::executor::block_on(self.execute(stmt))
}

pub async fn transaction(&self) -> Result<Transaction> {
let id = TRANSACTION_IDS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Transaction::new(self, id).await
Expand All @@ -136,10 +138,6 @@ impl Client {
}
}

pub fn execute_in_transaction_sync(&self, tx_id: u64, stmt: Statement) -> Result<ResultSet> {
futures::executor::block_on(self.execute_in_transaction(tx_id, stmt))
}

pub async fn commit_transaction(&self, tx_id: u64) -> Result<()> {
match self {
#[cfg(feature = "local_backend")]
Expand All @@ -155,10 +153,6 @@ impl Client {
}
}

pub fn commit_transaction_sync(&self, tx_id: u64) -> Result<()> {
futures::executor::block_on(self.commit_transaction(tx_id))
}

pub async fn rollback_transaction(&self, tx_id: u64) -> Result<()> {
match self {
#[cfg(feature = "local_backend")]
Expand All @@ -173,10 +167,6 @@ impl Client {
Self::Hrana(h) => h.rollback_transaction(tx_id).await,
}
}

pub fn rollback_transaction_sync(&self, tx_id: u64) -> Result<()> {
futures::executor::block_on(self.rollback_transaction(tx_id))
}
}

impl Client {
Expand All @@ -195,49 +185,44 @@ impl Client {
pub async fn from_config<'a>(config: Config) -> anyhow::Result<Client> {
let scheme = config.url.scheme();
Ok(match scheme {
#[cfg(feature = "local_backend")]
"file" => {
Client::Local(crate::local::Client::new(config.url.to_string())?)
},
#[cfg(feature = "hrana_backend")]
"ws" | "wss" => {
Client::Hrana(crate::hrana::Client::from_config(config).await?)
},
#[cfg(feature = "reqwest_backend")]
"libsql" => {
let inner = crate::http::InnerClient::Reqwest(crate::reqwest::HttpClient::new());
let mut config = config;
config.url = if config.url.scheme() == "libsql" {
// We cannot use url::Url::set_scheme() because it prevents changing the scheme to http...
// Safe to unwrap, because we know that the scheme is libsql
url::Url::parse(&config.url.as_str().replace("libsql://", "https://")).unwrap()
} else {
config.url
};
Client::Http(crate::http::Client::from_config(inner, config)?)
}
#[cfg(feature = "reqwest_backend")]
"http" | "https" => {
let inner = crate::http::InnerClient::Reqwest(crate::reqwest::HttpClient::new());
Client::Http(crate::http::Client::from_config(inner, config)?)
},
#[cfg(feature = "workers_backend")]
"workers" | "http" | "https" => {
let inner = crate::http::InnerClient::Workers(crate::workers::HttpClient::new());
Client::Http(crate::http::Client::from_config(inner, config)?)
},
#[cfg(feature = "spin_backend")]
"spin" | "http" | "https" => {
let inner = crate::http::InnerClient::Spin(crate::spin::HttpClient::new());
Client::Http(crate::http::Client::from_config(inner, config)?)
},
_ => anyhow::bail!("Unknown scheme: {scheme}. Make sure your backend exists and is enabled with its feature flag"),
})
}

/// A sync flavor of `from_config`
pub fn from_config_sync(config: Config) -> anyhow::Result<Client> {
futures::executor::block_on(Self::from_config(config))
#[cfg(feature = "local_backend")]
"file" => {
Client::Local(crate::local::Client::new(config.url.to_string())?)
},
#[cfg(feature = "hrana_backend")]
"ws" | "wss" => {
Client::Hrana(crate::hrana::Client::from_config(config).await?)
},
#[cfg(feature = "reqwest_backend")]
"libsql" => {
let inner = crate::http::InnerClient::Reqwest(crate::reqwest::HttpClient::new());
let mut config = config;
config.url = if config.url.scheme() == "libsql" {
// We cannot use url::Url::set_scheme() because it prevents changing the scheme to http...
// Safe to unwrap, because we know that the scheme is libsql
url::Url::parse(&config.url.as_str().replace("libsql://", "https://")).unwrap()
} else {
config.url
};
Client::Http(crate::http::Client::from_config(inner, config)?)
}
#[cfg(feature = "reqwest_backend")]
"http" | "https" => {
let inner = crate::http::InnerClient::Reqwest(crate::reqwest::HttpClient::new());
Client::Http(crate::http::Client::from_config(inner, config)?)
},
#[cfg(feature = "workers_backend")]
"workers" | "http" | "https" => {
let inner = crate::http::InnerClient::Workers(crate::workers::HttpClient::new());
Client::Http(crate::http::Client::from_config(inner, config)?)
},
#[cfg(feature = "spin_backend")]
"spin" | "http" | "https" => {
let inner = crate::http::InnerClient::Spin(crate::spin::HttpClient::new());
Client::Http(crate::http::Client::from_config(inner, config)?)
},
_ => anyhow::bail!("Unknown scheme: {scheme}. Make sure your backend exists and is enabled with its feature flag"),
})
}

/// Establishes a database client based on environment variables
Expand Down Expand Up @@ -269,11 +254,6 @@ impl Client {
.await
}

/// A sync flavor of `from_env`
pub fn from_env_sync() -> anyhow::Result<Client> {
futures::executor::block_on(Self::from_env())
}

#[cfg(feature = "workers_backend")]
pub fn from_workers_env(env: &worker::Env) -> anyhow::Result<Client> {
let url = env
Expand All @@ -295,6 +275,59 @@ impl Client {
}
}

pub mod sync {}
impl SyncClient {
pub fn from_config(config: Config) -> Result<Self> {
Ok(Self {
inner: futures::executor::block_on(Client::from_config(config))?,
})
}

pub fn from_env() -> Result<Self> {
Ok(Self {
inner: futures::executor::block_on(Client::from_env())?,
})
}

#[cfg(feature = "workers_backend")]
pub fn from_workers_env(env: &worker::Env) -> Result<Self> {
Ok(Self {
inner: Client::from_workers_env(env)?,
})
}

pub fn batch<I: IntoIterator<Item = impl Into<Statement> + Send> + Send>(
&self,
stmts: I,
) -> Result<Vec<ResultSet>>
where
<I as std::iter::IntoIterator>::IntoIter: std::marker::Send,
{
futures::executor::block_on(self.inner.batch(stmts))
}

pub fn execute(&self, stmt: impl Into<Statement> + Send) -> Result<ResultSet> {
futures::executor::block_on(self.inner.execute(stmt))
}

pub fn transaction(&self) -> Result<SyncTransaction> {
let id = TRANSACTION_IDS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
SyncTransaction::new(self, id)
}

pub fn execute_in_transaction(&self, tx_id: u64, stmt: Statement) -> Result<ResultSet> {
futures::executor::block_on(self.inner.execute_in_transaction(tx_id, stmt))
}

pub fn commit_transaction(&self, tx_id: u64) -> Result<()> {
futures::executor::block_on(self.inner.commit_transaction(tx_id))
}

pub fn rollback_transaction(&self, tx_id: u64) -> Result<()> {
futures::executor::block_on(self.inner.rollback_transaction(tx_id))
}
}

/// Configuration for the database client
pub struct Config {
pub url: url::Url,
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ impl std::convert::From<proto::StmtResult> for ResultSet {
}

pub mod client;
pub use client::{Client, Config};
pub use client::{Client, Config, SyncClient};

pub mod http;
pub mod transaction;
pub use transaction::Transaction;
pub use transaction::{SyncTransaction, Transaction};

#[cfg(feature = "workers_backend")]
pub mod workers;
Expand Down
49 changes: 37 additions & 12 deletions src/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! `Transaction` is a structure representing an interactive transaction.

use crate::{Client, ResultSet, Statement};
use crate::{Client, ResultSet, Statement, SyncClient};
use anyhow::Result;

pub struct Transaction<'a> {
Expand Down Expand Up @@ -49,23 +49,48 @@ impl<'a> Transaction<'a> {
pub async fn rollback(self) -> Result<()> {
self.client.rollback_transaction(self.id).await
}
}

pub fn new_sync(client: &'a Client, id: u64) -> Result<Transaction<'a>> {
client
.execute_in_transaction_sync(id, Statement::from("BEGIN"))
.map(|_| Self { id, client })
pub struct SyncTransaction<'a> {
pub(crate) id: u64,
pub(crate) client: &'a SyncClient,
}

impl<'a> SyncTransaction<'a> {
pub fn new(client: &'a SyncClient, id: u64) -> Result<SyncTransaction<'a>> {
client.execute_in_transaction(id, Statement::from("BEGIN"))?;
Ok(Self { id, client })
}

pub fn execute_sync(&self, stmt: impl Into<Statement>) -> Result<ResultSet> {
self.client
.execute_in_transaction_sync(self.id, stmt.into())
/// Executes a statement within the current transaction.
/// # Example
///
/// ```rust,no_run
/// # fn f() -> anyhow::Result<()> {
/// # use crate::libsql_client::{Statement, args};
/// let mut db = libsql_client::SyncClient::from_env()?;
/// let tx = db.transaction()?;
/// tx.execute(Statement::with_args("INSERT INTO users (name) VALUES (?)", args!["John"]))?;
/// let res = tx.execute(Statement::with_args("INSERT INTO users (name) VALUES (?)", args!["Jane"]));
/// if res.is_err() {
/// tx.rollback()?;
/// } else {
/// tx.commit()?;
/// }
/// # Ok(())
/// # }
/// ```
pub fn execute(&self, stmt: impl Into<Statement>) -> Result<ResultSet> {
self.client.execute_in_transaction(self.id, stmt.into())
}

pub fn commit_sync(self) -> Result<()> {
self.client.commit_transaction_sync(self.id)
/// Commits the transaction to the database.
pub fn commit(self) -> Result<()> {
self.client.commit_transaction(self.id)
}

pub fn rollback_sync(self) -> Result<()> {
self.client.rollback_transaction_sync(self.id)
/// Rolls back the transaction, cancelling any of its side-effects.
pub fn rollback(self) -> Result<()> {
self.client.rollback_transaction(self.id)
}
}

0 comments on commit 9f4ec9b

Please sign in to comment.