use std::{any::Any, collections::HashMap, sync::Arc};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::errors::RvError;
pub mod barrier;
pub mod barrier_aes_gcm;
pub mod barrier_view;
pub mod physical;
pub mod sql;
#[cfg(feature = "storage_xline")]
pub mod xline;
#[async_trait]
pub trait Storage: Send + Sync {
async fn list(&self, prefix: &str) -> Result<Vec<String>, RvError>;
async fn get(&self, key: &str) -> Result<Option<StorageEntry>, RvError>;
async fn put(&self, entry: &StorageEntry) -> Result<(), RvError>;
async fn delete(&self, key: &str) -> Result<(), RvError>;
async fn lock(&self, _lock_name: &str) -> Result<Box<dyn Any>, RvError> {
Ok(Box::new(true))
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct StorageEntry {
pub key: String,
pub value: Vec<u8>,
}
impl StorageEntry {
pub fn new(k: &str, v: &impl Serialize) -> Result<StorageEntry, RvError> {
let data = serde_json::to_string(v)?;
Ok(StorageEntry {
key: k.to_string(),
value: data.into_bytes(),
})
}
}
#[async_trait]
pub trait Backend: Send + Sync {
async fn list(&self, prefix: &str) -> Result<Vec<String>, RvError>;
async fn get(&self, key: &str) -> Result<Option<BackendEntry>, RvError>;
async fn put(&self, entry: &BackendEntry) -> Result<(), RvError>;
async fn delete(&self, key: &str) -> Result<(), RvError>;
async fn lock(&self, _lock_name: &str) -> Result<Box<dyn Any>, RvError> {
Ok(Box::new(true))
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BackendEntry {
pub key: String,
pub value: Vec<u8>,
}
#[cfg(feature = "storage_sqlite")]
fn current_handle<'a, F, T>(fut: F) -> T
where
F: std::future::Future<Output = T> + Send + 'a,
T: Send + 'a,
{
match tokio::runtime::Handle::try_current() {
Ok(_) => std::thread::scope(|s| {
s.spawn(|| {
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
rt.block_on(fut)
})
.join()
.expect("Couldn't join on the associated thread")
}),
Err(_) => {
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
rt.block_on(fut)
}
}
}
pub fn new_backend(t: &str, conf: &HashMap<String, Value>) -> Result<Arc<dyn Backend>, RvError> {
match t {
"file" => {
let backend = physical::file::FileBackend::new(conf)?;
Ok(Arc::new(backend))
}
#[cfg(feature = "storage_xline")]
"xline" => {
let backend = xline::XlineBackend::new(conf)?;
Ok(Arc::new(backend))
}
#[cfg(feature = "storage_sqlite")]
"sqlite" => {
let backend = current_handle(sql::sqlite::SqliteBackend::new(conf))?;
Ok(Arc::new(backend))
}
"mock" => Ok(Arc::new(physical::mock::MockBackend::new())),
_ => Err(RvError::ErrPhysicalTypeInvalid),
}
}