pub mod errors;
pub mod in_memory;
pub use errors::StorageError;
pub use in_memory::{InMemoryStorage, InMemoryStorageStats};
use serde_json::Value;
use std::fmt;
use std::future::Future;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StorageKey {
tenant_id: String,
resource_type: String,
resource_id: String,
}
impl StorageKey {
pub fn new(
tenant_id: impl Into<String>,
resource_type: impl Into<String>,
resource_id: impl Into<String>,
) -> Self {
Self {
tenant_id: tenant_id.into(),
resource_type: resource_type.into(),
resource_id: resource_id.into(),
}
}
pub fn tenant_id(&self) -> &str {
&self.tenant_id
}
pub fn resource_type(&self) -> &str {
&self.resource_type
}
pub fn resource_id(&self) -> &str {
&self.resource_id
}
pub fn prefix(tenant_id: impl Into<String>, resource_type: impl Into<String>) -> StoragePrefix {
StoragePrefix {
tenant_id: tenant_id.into(),
resource_type: resource_type.into(),
}
}
}
impl fmt::Display for StorageKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}/{}/{}",
self.tenant_id, self.resource_type, self.resource_id
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StoragePrefix {
tenant_id: String,
resource_type: String,
}
impl StoragePrefix {
pub fn tenant_id(&self) -> &str {
&self.tenant_id
}
pub fn resource_type(&self) -> &str {
&self.resource_type
}
}
impl fmt::Display for StoragePrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.tenant_id, self.resource_type)
}
}
pub trait StorageProvider: Send + Sync {
type Error: std::error::Error + Send + Sync + 'static;
fn put(
&self,
key: StorageKey,
data: Value,
) -> impl Future<Output = Result<Value, Self::Error>> + Send;
fn get(
&self,
key: StorageKey,
) -> impl Future<Output = Result<Option<Value>, Self::Error>> + Send;
fn delete(&self, key: StorageKey) -> impl Future<Output = Result<bool, Self::Error>> + Send;
fn list(
&self,
prefix: StoragePrefix,
offset: usize,
limit: usize,
) -> impl Future<Output = Result<Vec<(StorageKey, Value)>, Self::Error>> + Send;
fn find_by_attribute(
&self,
prefix: StoragePrefix,
attribute: &str,
value: &str,
) -> impl Future<Output = Result<Vec<(StorageKey, Value)>, Self::Error>> + Send;
fn exists(&self, key: StorageKey) -> impl Future<Output = Result<bool, Self::Error>> + Send;
fn count(
&self,
prefix: StoragePrefix,
) -> impl Future<Output = Result<usize, Self::Error>> + Send;
fn list_tenants(&self) -> impl Future<Output = Result<Vec<String>, Self::Error>> + Send;
fn list_resource_types(
&self,
tenant_id: &str,
) -> impl Future<Output = Result<Vec<String>, Self::Error>> + Send;
fn list_all_resource_types(&self) -> impl Future<Output = Result<Vec<String>, Self::Error>> + Send;
fn clear(&self) -> impl Future<Output = Result<(), Self::Error>> + Send;
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_storage_key() {
let key = StorageKey::new("tenant1", "User", "123");
assert_eq!(key.tenant_id(), "tenant1");
assert_eq!(key.resource_type(), "User");
assert_eq!(key.resource_id(), "123");
assert_eq!(key.to_string(), "tenant1/User/123");
}
#[tokio::test]
async fn test_storage_prefix() {
let prefix = StorageKey::prefix("tenant1", "User");
assert_eq!(prefix.tenant_id(), "tenant1");
assert_eq!(prefix.resource_type(), "User");
assert_eq!(prefix.to_string(), "tenant1/User");
}
}