use redb::TableDefinition;
use rong::{function::Optional, *};
use std::path::PathBuf;
mod storage;
pub use storage::*;
pub(crate) const STORAGE_TABLE: TableDefinition<&str, &[u8]> = TableDefinition::new("storage");
const REDB_INIT_OVERHEAD: usize = (1.5 * 1024.0 * 1024.0) as usize; pub(crate) const DEFAULT_MAX_TOTAL_SIZE: usize = 20 * 1024 * 1024; pub(crate) const DEFAULT_MAX_USER_DATA_SIZE: usize = DEFAULT_MAX_TOTAL_SIZE + REDB_INIT_OVERHEAD;
pub(crate) const DEFAULT_MAX_KEY_SIZE: usize = 1024; pub(crate) const DEFAULT_MAX_VALUE_SIZE: usize = 5 * 1024 * 1024;
#[derive(IntoJSObj)]
pub struct StorageInfo {
#[rename = "currentSize"]
pub(crate) current_size: u32,
#[rename = "limitSize"]
pub(crate) limit_size: u32,
#[rename = "keyCount"]
pub(crate) key_count: u32,
}
async fn storage_open(
ctx: JSContext,
path: String,
options: Optional<StorageOptionsInput>,
) -> JSResult<JSObject> {
let opts = options.0.map(StorageOptions::from).unwrap_or_default();
let storage = Storage::open_with_options(PathBuf::from(path), opts)?;
Ok(Class::get::<Storage>(&ctx)?.instance(storage))
}
pub fn init(ctx: &JSContext) -> JSResult<()> {
ctx.register_class::<Storage>()?;
let constructor = Class::get::<Storage>(ctx)?;
let rong = ctx.rong();
rong.set("Storage", constructor.clone())?;
let storage_ns = JSObject::new(ctx);
storage_ns.set("open", JSFunc::new(ctx, storage_open)?.name("open")?)?;
rong.set("storage", storage_ns)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use rong_test::*;
use std::env;
#[test]
fn test_storage() {
async_run!(|ctx: JSContext| async move {
let workspace_root = env::current_dir()
.map_err(|e| {
HostError::new(
rong::error::E_INTERNAL,
format!("Failed to get current dir: {}", e),
)
})?
.parent()
.and_then(|p| p.parent()) .ok_or_else(|| {
HostError::new(rong::error::E_INTERNAL, "Failed to get workspace root")
})?
.to_string_lossy()
.into_owned();
let storage_path = format!("{}/target/test-tmp/test_storage.db", workspace_root);
ctx.global().set("TEST_STORAGE_DB_PATH", storage_path)?;
rong_assert::init(&ctx)?;
rong_console::init(&ctx)?;
init(&ctx)?;
let passed = UnitJSRunner::load_script(&ctx, "storage.js")
.await?
.run()
.await?;
assert!(passed);
Ok(())
});
}
#[test]
fn rust_open_api_provides_handles() {
let workspace_root = env::current_dir()
.expect("cwd")
.parent()
.and_then(|p| p.parent())
.expect("workspace root")
.to_path_buf();
let default_path = workspace_root.join("target/test-tmp/rust_storage_default.db");
Storage::open(default_path).expect("default open should succeed");
let custom_path = workspace_root.join("target/test-tmp/rust_storage_custom.db");
let options = StorageOptions {
max_key_size: Some(16),
..Default::default()
};
Storage::open_with_options(custom_path, options).expect("custom open should succeed");
}
#[test]
fn close_allows_reopen_same_path() {
let workspace_root = env::current_dir()
.expect("cwd")
.parent()
.and_then(|p| p.parent())
.expect("workspace root")
.to_path_buf();
let path = workspace_root.join("target/test-tmp/rust_storage_reopen.db");
let storage = Storage::open(&path).expect("initial open should succeed");
storage.close();
let _storage2 =
Storage::open(&path).expect("reopen after close should succeed without lock error");
}
}