use std::path::Path;
use std::sync::OnceLock;
use super::{artifact_reads_switched_on, module_from_header, read_header_from, CacheKey};
use crate::module_artifact::ModuleArtifact;
#[derive(Debug, Clone, Copy)]
pub struct EmbeddedStdlibArtifact {
pub file_name: &'static str,
pub bytes: &'static [u8],
}
static EMBEDDED_STDLIB_ARTIFACTS: OnceLock<&'static [EmbeddedStdlibArtifact]> = OnceLock::new();
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EmbeddedStdlibRegistrationError {
AlreadyRegistered,
Unsorted(&'static str),
}
pub fn register_embedded_stdlib_artifacts(
table: &'static [EmbeddedStdlibArtifact],
) -> Result<(), EmbeddedStdlibRegistrationError> {
if let Some(pair) = table
.windows(2)
.find(|pair| pair[0].file_name >= pair[1].file_name)
{
return Err(EmbeddedStdlibRegistrationError::Unsorted(pair[1].file_name));
}
EMBEDDED_STDLIB_ARTIFACTS
.set(table)
.map_err(|_| EmbeddedStdlibRegistrationError::AlreadyRegistered)
}
pub(crate) fn load_embedded_stdlib_module(
key: &CacheKey,
synthetic: &Path,
) -> Option<ModuleArtifact> {
if !artifact_reads_switched_on() {
return None;
}
lookup(EMBEDDED_STDLIB_ARTIFACTS.get()?, key, synthetic)
}
pub(crate) fn lookup(
table: &[EmbeddedStdlibArtifact],
key: &CacheKey,
synthetic: &Path,
) -> Option<ModuleArtifact> {
let name = key.module_filename();
let index = table
.binary_search_by(|entry| entry.file_name.cmp(name.as_str()))
.ok()?;
read_module_from_bytes(table[index].bytes, key, synthetic)
}
fn read_module_from_bytes(
bytes: &[u8],
key: &CacheKey,
synthetic: &Path,
) -> Option<ModuleArtifact> {
read_header_from(bytes, key, Some(&key.context_hash))
.and_then(|header| module_from_header(header, synthetic))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::module_artifact::ModuleProvenance;
use crate::module_source::ModuleSource;
fn leak(rows: Vec<(String, Vec<u8>)>) -> &'static [EmbeddedStdlibArtifact] {
let rows = rows
.into_iter()
.map(|(name, bytes)| EmbeddedStdlibArtifact {
file_name: Box::leak(name.into_boxed_str()),
bytes: Box::leak(bytes.into_boxed_slice()),
})
.collect::<Vec<_>>();
Box::leak(rows.into_boxed_slice())
}
fn key_for(module: &str) -> CacheKey {
let source = harn_modules::stdlib_catalog()
.source(module)
.expect("stdlib module exists");
CacheKey::from_embedded_stdlib_module_content_hash(
ModuleSource::from_text(source).sha256(),
ModuleProvenance::EmbeddedStdlib,
)
}
#[test]
fn a_compiled_artifact_answers_its_own_key_and_refuses_another_build() {
let module = "agent/loop";
let source = harn_modules::stdlib_catalog()
.source(module)
.expect("stdlib module exists");
let compiled =
crate::vm::compile_stdlib_module_artifact(module, source).expect("agent/loop compiles");
let key = key_for(module);
let table = leak(vec![(compiled.file_name.clone(), compiled.bytes.clone())]);
let synthetic = Path::new("<stdlib>/agent/loop.harn");
assert!(
lookup(table, &key, synthetic).is_some(),
"the producer and the loader must agree on the key"
);
let other_build = key.clone().for_artifact_version("0.0.0-other");
let artifact = read_module_from_bytes(&compiled.bytes, &key, synthetic).expect("decodes");
let foreign = leak(vec![(
key.module_filename(),
super::super::serialize_module_artifact(&other_build, &artifact).expect("encodes"),
)]);
assert!(lookup(foreign, &key, synthetic).is_none());
}
#[test]
fn registration_refuses_an_unsorted_table() {
let table = leak(vec![
("b".to_string(), Vec::new()),
("a".to_string(), Vec::new()),
]);
assert_eq!(
register_embedded_stdlib_artifacts(table),
Err(EmbeddedStdlibRegistrationError::Unsorted("a"))
);
assert!(EMBEDDED_STDLIB_ARTIFACTS.get().is_none());
}
}