use std::sync::Arc;
use async_trait::async_trait;
#[derive(Debug, Clone, PartialEq)]
pub enum SqlValue {
Null,
Boolean(bool),
Integer(i64),
Real(f64),
Text(String),
Blob(Vec<u8>),
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct SqlRows {
pub columns: Vec<String>,
pub rows: Vec<Vec<SqlValue>>,
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum SqlError {
#[error("sql syntax error: {0}")]
Syntax(String),
#[error("sql constraint error: {0}")]
Constraint(String),
#[error("sql error: {0}")]
Other(String),
}
impl SqlError {
pub fn other<E: std::fmt::Display>(err: E) -> Self {
Self::Other(err.to_string())
}
}
#[async_trait]
pub trait SqlBackend: Send + Sync {
async fn begin(&self) -> Result<Box<dyn SqlTransaction>, SqlError>;
async fn begin_read_only(&self) -> Result<Box<dyn SqlTransaction>, SqlError> {
self.begin().await
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PreviewSqlMode {
#[default]
Empty,
Branch,
Shared,
}
#[async_trait]
pub trait SqlBackends: Send + Sync {
async fn database(
&self,
project: &str,
site: &str,
name: &str,
) -> Result<Arc<dyn SqlBackend>, SqlError>;
async fn preview_database(
&self,
project: &str,
site: &str,
name: &str,
preview: &str,
) -> Result<Arc<dyn SqlBackend>, SqlError> {
let qualified = crate::project::ProjectRef::new(project).qualified(site);
self.database(
crate::project::DEFAULT_PROJECT,
&format!("{qualified}/_preview/{preview}"),
name,
)
.await
}
}
#[async_trait]
pub trait SqlTransaction: Send {
async fn query(&mut self, sql: &str, params: &[SqlValue]) -> Result<SqlRows, SqlError>;
async fn execute(&mut self, sql: &str, params: &[SqlValue]) -> Result<u64, SqlError>;
async fn commit(self: Box<Self>) -> Result<(), SqlError>;
async fn rollback(self: Box<Self>) -> Result<(), SqlError>;
}