use std::{future::Future, pin::Pin};
use fraiseql_error::Result;
pub trait StorageBackend: Send + Sync {
fn get(
&self,
bucket: &str,
key: &str,
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>>> + Send + '_>>;
fn put(
&self,
bucket: &str,
key: &str,
body: &[u8],
content_type: &str,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>;
}
#[cfg(test)]
pub struct MockStorageBackend {
data: std::sync::Arc<
std::sync::Mutex<
std::collections::HashMap<String, std::collections::HashMap<String, Vec<u8>>>,
>,
>,
}
#[cfg(test)]
impl MockStorageBackend {
#[must_use]
pub fn new() -> std::sync::Arc<Self> {
std::sync::Arc::new(Self {
data: std::sync::Arc::new(std::sync::Mutex::new(std::collections::HashMap::new())),
})
}
pub fn store(&self, bucket: &str, key: &str, data: Vec<u8>) {
let mut storage = self.data.lock().expect("storage lock poisoned");
storage.entry(bucket.to_string()).or_default().insert(key.to_string(), data);
}
#[must_use]
pub fn get_stored(&self, bucket: &str, key: &str) -> Option<Vec<u8>> {
let storage = self.data.lock().expect("storage lock poisoned");
storage.get(bucket).and_then(|bucket_data| bucket_data.get(key)).cloned()
}
}
#[cfg(test)]
impl StorageBackend for MockStorageBackend {
fn get(
&self,
bucket: &str,
key: &str,
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>>> + Send + '_>> {
let bucket = bucket.to_string();
let key = key.to_string();
let storage = self.data.clone();
Box::pin(async move {
let data = storage.lock().expect("storage lock poisoned");
data.get(&bucket)
.and_then(|bucket_data| bucket_data.get(&key))
.cloned()
.ok_or_else(|| {
fraiseql_error::FraiseQLError::File(fraiseql_error::FileError::NotFound {
id: format!("{bucket}/{key}"),
})
})
})
}
fn put(
&self,
bucket: &str,
key: &str,
body: &[u8],
_content_type: &str,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>> {
let bucket = bucket.to_string();
let key = key.to_string();
let body = body.to_vec();
let storage = self.data.clone();
Box::pin(async move {
let mut data = storage.lock().expect("storage lock poisoned");
data.entry(bucket).or_default().insert(key, body);
Ok(())
})
}
}