use async_trait::async_trait;
use serde_json::Value;
use std::any::Any;
use std::fmt::{Debug, Formatter, Result as StdResult};
use std::result::Result;
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackendType {
Native,
Supabase,
Postgrest,
Scylla,
Neon,
PostgreSQL,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QueryLanguage {
Sql,
Cql,
Postgrest,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PostgrestMethod {
Get,
Post,
Patch,
Delete,
}
#[derive(Debug, Clone)]
pub struct TranslatedQuery {
pub sql: String,
pub language: QueryLanguage,
pub params: Vec<Value>,
pub table: Option<String>,
pub postgrest_method: Option<PostgrestMethod>,
pub payload: Option<Value>,
}
impl TranslatedQuery {
pub fn new(
sql: impl Into<String>,
language: QueryLanguage,
params: Vec<Value>,
table: Option<String>,
) -> Self {
Self {
sql: sql.into(),
language,
params,
table,
postgrest_method: None,
payload: None,
}
}
pub fn sql(sql: impl Into<String>, params: Vec<Value>, table: Option<String>) -> Self {
Self::new(sql, QueryLanguage::Sql, params, table)
}
pub fn cql(sql: impl Into<String>, params: Vec<Value>, table: Option<String>) -> Self {
Self::new(sql, QueryLanguage::Cql, params, table)
}
pub fn postgrest(sql: impl Into<String>, params: Vec<Value>, table: Option<String>) -> Self {
Self::new(sql, QueryLanguage::Postgrest, params, table)
}
pub fn with_postgrest_method(mut self, method: PostgrestMethod) -> Self {
self.postgrest_method = Some(method);
self
}
pub fn with_payload(mut self, payload: Value) -> Self {
self.payload = Some(payload);
self
}
}
#[derive(Clone)]
pub struct QueryResult {
pub rows: Vec<Value>,
pub columns: Vec<String>,
pub duration: Option<u64>,
pub message: Option<String>,
pub status: Option<String>,
pub count: Option<u64>,
}
impl Debug for QueryResult {
fn fmt(&self, f: &mut Formatter<'_>) -> StdResult {
f.debug_struct("QueryResult")
.field("rows", &self.rows)
.field("columns", &self.columns)
.field("count", &self.count)
.finish()
}
}
impl QueryResult {
pub fn new(
rows: Vec<Value>,
columns: Vec<String>,
duration: Option<u64>,
message: Option<String>,
status: Option<String>,
) -> Self {
Self {
rows,
columns,
duration,
message,
status,
count: None,
}
}
pub fn with_count(mut self, count: Option<u64>) -> Self {
self.count = count;
self
}
}
pub enum HealthStatus {
Healthy,
Offline(Instant),
}
pub type BackendResult<T> = Result<T, BackendError>;
#[derive(thiserror::Error, Debug)]
pub enum BackendError {
#[error("backend error: {0}")]
Generic(String),
}
#[async_trait]
pub trait DatabaseBackend: Send + Sync {
async fn execute_query(&self, query: TranslatedQuery) -> BackendResult<QueryResult>;
async fn health_check(&self) -> BackendResult<HealthStatus>;
fn backend_type(&self) -> BackendType;
fn supports_sql(&self) -> bool;
fn supports_cql(&self) -> bool;
fn as_any(&self) -> &dyn Any;
}