use std::sync::Arc;
use crate::{DurabilityTier, LashlangArtifactStore, ModuleArtifact, parse};
pub struct ReopenableLashlangArtifactStore {
pub open: Arc<dyn LashlangArtifactStore>,
pub reopen: Arc<dyn LashlangArtifactStore>,
}
fn sample_module_artifact(source: &str) -> ModuleArtifact {
let program = parse(source).expect("parse sample lashlang module");
ModuleArtifact::from_program(program).expect("build sample module artifact")
}
pub async fn lashlang_artifact_store<F>(make: F, expected_tier: DurabilityTier)
where
F: Fn() -> Arc<dyn LashlangArtifactStore>,
{
assert_eq!(
make().durability_tier(),
expected_tier,
"lashlang artifact store must report its declared durability tier"
);
module_artifact_round_trips(make()).await;
raw_artifact_round_trips(make()).await;
raw_artifact_overwrite(make()).await;
module_and_raw_namespaces_isolate(make()).await;
}
pub async fn lashlang_artifact_store_reopenable<F>(make: F)
where
F: Fn() -> ReopenableLashlangArtifactStore,
{
lashlang_artifact_store(|| make().open, DurabilityTier::Durable).await;
survives_reopen(make()).await;
}
async fn module_artifact_round_trips(store: Arc<dyn LashlangArtifactStore>) {
let artifact = sample_module_artifact("process alpha(root: str) -> str { finish root }");
assert!(
store
.get_module_artifact(&artifact.module_ref)
.await
.expect("get missing module artifact")
.is_none(),
"a fresh store must not resolve an unwritten module artifact"
);
store
.put_module_artifact(&artifact)
.await
.expect("put module artifact");
let loaded = store
.get_module_artifact(&artifact.module_ref)
.await
.expect("get module artifact")
.expect("module artifact present after put");
assert_eq!(*loaded, artifact, "module artifact must round-trip");
store
.put_module_artifact(&artifact)
.await
.expect("re-put module artifact");
let reloaded = store
.get_module_artifact(&artifact.module_ref)
.await
.expect("get module artifact after re-put")
.expect("module artifact present after re-put");
assert_eq!(*reloaded, artifact);
}
async fn raw_artifact_round_trips(store: Arc<dyn LashlangArtifactStore>) {
assert!(
store
.get_artifact_bytes("raw:missing")
.await
.expect("get missing raw artifact")
.is_none(),
"a fresh store must not resolve unwritten raw artifact bytes"
);
store
.put_artifact_bytes("raw:sample", "generic", b"raw-artifact-payload")
.await
.expect("put raw artifact bytes");
assert_eq!(
store
.get_artifact_bytes("raw:sample")
.await
.expect("get raw artifact bytes"),
Some(b"raw-artifact-payload".to_vec()),
"raw artifact bytes must round-trip"
);
}
async fn raw_artifact_overwrite(store: Arc<dyn LashlangArtifactStore>) {
store
.put_artifact_bytes("raw:overwrite", "generic", b"first")
.await
.expect("put first raw bytes");
store
.put_artifact_bytes("raw:overwrite", "generic", b"second")
.await
.expect("overwrite raw bytes");
assert_eq!(
store
.get_artifact_bytes("raw:overwrite")
.await
.expect("get overwritten raw bytes"),
Some(b"second".to_vec()),
"re-putting a raw artifact key must overwrite its bytes"
);
}
async fn module_and_raw_namespaces_isolate(store: Arc<dyn LashlangArtifactStore>) {
let artifact = sample_module_artifact("process gamma(root: str) -> str { finish root }");
let colliding_key = artifact.module_ref.as_str().to_string();
store
.put_module_artifact(&artifact)
.await
.expect("put module artifact");
store
.put_artifact_bytes(&colliding_key, "generic", b"not-a-module-artifact")
.await
.expect("put raw bytes at the module key");
let module = store
.get_module_artifact(&artifact.module_ref)
.await
.expect("module artifact survives raw write at the same key")
.expect("module artifact still present");
assert_eq!(*module, artifact);
assert_eq!(
store
.get_artifact_bytes(&colliding_key)
.await
.expect("raw bytes survive module write at the same key"),
Some(b"not-a-module-artifact".to_vec()),
);
}
async fn survives_reopen(reopenable: ReopenableLashlangArtifactStore) {
let ReopenableLashlangArtifactStore { open, reopen } = reopenable;
let artifact = sample_module_artifact("process epsilon(root: str) -> str { finish root }");
open.put_module_artifact(&artifact)
.await
.expect("put module artifact");
open.put_artifact_bytes("raw:reopen", "generic", b"raw-reopen")
.await
.expect("put raw bytes");
let module = reopen
.get_module_artifact(&artifact.module_ref)
.await
.expect("get module artifact after reopen")
.expect("module artifact survives reopen");
assert_eq!(*module, artifact);
assert_eq!(
reopen
.get_artifact_bytes("raw:reopen")
.await
.expect("get raw bytes after reopen"),
Some(b"raw-reopen".to_vec()),
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::InMemoryLashlangArtifactStore;
#[tokio::test]
async fn in_memory_lashlang_artifact_store_satisfies_conformance() {
lashlang_artifact_store(
|| Arc::new(InMemoryLashlangArtifactStore::new()) as Arc<dyn LashlangArtifactStore>,
DurabilityTier::Inline,
)
.await;
}
}