///|
/// IO Error types for storage operations
pub(all) suberror IOError {
NotFound(String)
PermissionDenied(String)
IoFailed(String)
}
///|
/// Storage kind - tells app what type of data is being stored
/// App uses this to route to appropriate backend (e.g., index→DynamoDB, data→S3)
pub(all) enum StorageKind {
Config // Collection configuration (.config.json)
Index // Index structures, manifests (.index, .manifest.json)
Data // Vector data, segments (.data.bin, segment files)
}
///|
/// Storage trait - abstraction for file-like storage operations
/// kind parameter allows app to route different data types to different backends
pub trait Storage {
fn read(Self, String, StorageKind) -> Bytes raise IOError
fn write(Self, String, Bytes, StorageKind) -> Unit raise IOError
fn append(Self, String, Bytes, StorageKind) -> Unit raise IOError
fn atomic_write(Self, String, Bytes, StorageKind) -> Unit raise IOError
fn del(Self, String, StorageKind) -> Unit raise IOError
fn exists(Self, String, StorageKind) -> Bool
fn list(Self, StorageKind) -> Array[String] raise IOError
}
///|
/// Memory-based storage implementation for testing and in-memory use cases
pub struct MemoryStorage {
data : Map[String, Bytes]
}
///|
/// Create a new empty MemoryStorage
pub fn MemoryStorage::new() -> MemoryStorage {
{ data: {} }
}
///|
/// Read file contents
pub fn MemoryStorage::read(
self : MemoryStorage,
path : String,
) -> Bytes raise IOError {
match self.data.get(path) {
None => raise NotFound(path)
Some(data) => data
}
}
///|
/// Write file contents (overwrites)
pub fn MemoryStorage::write(
self : MemoryStorage,
path : String,
data : Bytes,
) -> Unit {
self.data.set(path, data)
}
///|
/// Append data to file
pub fn MemoryStorage::append(
self : MemoryStorage,
path : String,
data : Bytes,
) -> Unit {
match self.data.get(path) {
None => self.data.set(path, data)
Some(existing) => self.data.set(path, concat_bytes(existing, data))
}
}
///|
/// Atomic write (same as write for in-memory implementation)
pub fn MemoryStorage::atomic_write(
self : MemoryStorage,
path : String,
data : Bytes,
) -> Unit {
self.data.set(path, data)
}
///|
/// Delete a file
pub fn MemoryStorage::del(
self : MemoryStorage,
path : String,
) -> Unit raise IOError {
if self.data.contains(path) {
self.data.remove(path)
} else {
raise NotFound(path)
}
}
///|
/// Check if a file exists
pub fn MemoryStorage::exists(self : MemoryStorage, path : String) -> Bool {
self.data.contains(path)
}
///|
/// List all files
pub fn MemoryStorage::list(self : MemoryStorage) -> Array[String] {
self.data.keys().collect()
}
///|
/// Clear all files
pub fn MemoryStorage::clear(self : MemoryStorage) -> Unit {
self.data.clear()
}
///|
/// Storage trait implementation for MemoryStorage
/// MemoryStorage ignores kind - stores all data in single map
pub impl Storage for MemoryStorage with fn read(self, path, _kind) {
self.read(path)
}
///|
pub impl Storage for MemoryStorage with fn write(self, path, data, _kind) {
self.write(path, data)
}
///|
pub impl Storage for MemoryStorage with fn append(self, path, data, _kind) {
self.append(path, data)
}
///|
pub impl Storage for MemoryStorage with fn atomic_write(self, path, data, _kind) {
self.atomic_write(path, data)
}
///|
pub impl Storage for MemoryStorage with fn del(self, path, _kind) {
self.del(path)
}
///|
pub impl Storage for MemoryStorage with fn exists(self, path, _kind) {
self.exists(path)
}
///|
pub impl Storage for MemoryStorage with fn list(self, _kind) {
self.list()
}
///|
/// AsyncStorage trait implementation for MemoryStorage.
/// Since all operations are in-memory, resolve is called synchronously.
/// This enables MemoryStorage to be used with PersistentVectorDB, AsyncWalRuntime,
/// and all async persistence infrastructure — critical for testing.
pub impl AsyncStorage for MemoryStorage with fn async_read(
self,
path,
_kind,
resolve,
reject,
) {
match self.data.get(path) {
None => reject("Not found: " + path)
Some(data) => resolve(data)
}
}
///|
pub impl AsyncStorage for MemoryStorage with fn async_write(
self,
path,
data,
_kind,
resolve,
_reject,
) {
self.write(path, data)
resolve()
}
///|
pub impl AsyncStorage for MemoryStorage with fn async_atomic_write(
self,
path,
data,
_kind,
resolve,
_reject,
) {
self.atomic_write(path, data)
resolve()
}
///|
pub impl AsyncStorage for MemoryStorage with fn async_del(
self,
path,
_kind,
resolve,
reject,
) {
if self.data.contains(path) {
self.data.remove(path)
resolve()
} else {
reject("Not found: " + path)
}
}
///|
pub impl AsyncStorage for MemoryStorage with fn async_exists(
self,
path,
_kind,
resolve,
_reject,
) {
resolve(self.exists(path))
}
///|
pub impl AsyncStorage for MemoryStorage with fn async_list(
self,
_kind,
resolve,
_reject,
) {
resolve(self.list())
}