///|
async fn[T, E : Error] suspend(
f : ((T) -> Unit, (E) -> Unit) -> Unit,
) -> T raise E = "%async.suspend"
///|
/// Async Storage trait — target-independent asynchronous storage abstraction.
///
/// Unlike the synchronous Storage trait, operations complete asynchronously
/// via MoonBit's native async/await mechanism (`%async.suspend`).
/// This works on ALL targets: JS (via Promise bridge), native (via event loop),
/// Wasm (via host callbacks).
///
/// For JS environments, the FFI layer converts JS Promises into suspend/resume.
/// For native environments, the FFI layer converts C callbacks into suspend/resume.
/// The persistence layer (WAL, snapshots, DualAsyncPersistence) is target-agnostic.
///
/// Each method takes a callback pair (resolve, reject) to signal completion.
/// The async fn wrappers in the runtime use %async.suspend to await these.
///|
/// Async storage trait — all targets.
/// All operations return results via MoonBit's async/await mechanism.
/// Implementors must call the provided continuation when I/O completes.
pub(open) trait AsyncStorage {
/// Read file contents. Calls resolve with data, or reject on error.
/// Missing files must reject rather than resolving empty Bytes.
fn async_read(Self, String, StorageKind, (Bytes) -> Unit, (String) -> Unit) -> Unit
/// Write data to file (overwrites existing).
fn async_write(Self, String, Bytes, StorageKind, () -> Unit, (String) -> Unit) -> Unit
/// Atomic write (same as write for most backends, but guarantees atomicity).
fn async_atomic_write(
Self,
String,
Bytes,
StorageKind,
() -> Unit,
(String) -> Unit,
) -> Unit
/// Delete file.
fn async_del(Self, String, StorageKind, () -> Unit, (String) -> Unit) -> Unit
/// Check if file exists.
fn async_exists(Self, String, StorageKind, (Bool) -> Unit, (String) -> Unit) -> Unit
/// List all files for a given kind. Rejects on backend failures.
fn async_list(Self, StorageKind, (Array[String]) -> Unit, (String) -> Unit) -> Unit
}
///|
/// Error type for async storage operations.
pub(all) suberror AsyncIOError {
AsyncIOFailed(String)
}
///|
/// Convenience async wrappers that use %async.suspend to bridge
/// callback-based AsyncStorage trait to MoonBit async/await.
///|
pub async fn[S : AsyncStorage] async_read(
storage : S,
path : String,
kind : StorageKind,
) -> Bytes {
suspend(fn(resolve, reject) {
storage.async_read(path, kind, resolve, fn(err) {
reject(AsyncIOFailed(err))
})
})
}
///|
pub async fn[S : AsyncStorage] async_write(
storage : S,
path : String,
data : Bytes,
kind : StorageKind,
) -> Unit {
suspend(fn(resolve, reject) {
storage.async_write(path, data, kind, fn() { resolve(()) }, fn(err) {
reject(AsyncIOFailed(err))
})
})
}
///|
pub async fn[S : AsyncStorage] async_atomic_write(
storage : S,
path : String,
data : Bytes,
kind : StorageKind,
) -> Unit {
suspend(fn(resolve, reject) {
storage.async_atomic_write(path, data, kind, fn() { resolve(()) }, fn(err) {
reject(AsyncIOFailed(err))
})
})
}
///|
pub async fn[S : AsyncStorage] async_del(
storage : S,
path : String,
kind : StorageKind,
) -> Unit {
suspend(fn(resolve, reject) {
storage.async_del(path, kind, fn() { resolve(()) }, fn(err) {
reject(AsyncIOFailed(err))
})
})
}
///|
pub async fn[S : AsyncStorage] async_exists(
storage : S,
path : String,
kind : StorageKind,
) -> Bool {
suspend(fn(resolve, reject) {
storage.async_exists(path, kind, resolve, fn(err) {
reject(AsyncIOFailed(err))
})
})
}
///|
pub async fn[S : AsyncStorage] async_list(
storage : S,
kind : StorageKind,
) -> Array[String] {
suspend(fn(resolve, reject) {
storage.async_list(kind, resolve, fn(err) { reject(AsyncIOFailed(err)) })
})
}