use std::sync::{LazyLock, OnceLock};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StdlibSource {
pub module: &'static str,
pub source: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StdlibPromptAsset {
pub path: &'static str,
pub source: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StdlibCliScript {
pub name: &'static str,
pub source: &'static str,
}
#[derive(Debug)]
pub struct StdlibCatalog {
pub sources: &'static [StdlibSource],
pub prompt_assets: &'static [StdlibPromptAsset],
pub cli_scripts: &'static [StdlibCliScript],
}
impl StdlibCatalog {
pub fn source(&self, module: &str) -> Option<&'static str> {
self.sources
.iter()
.find_map(|entry| (entry.module == module).then_some(entry.source))
}
pub fn prompt_asset(&self, path: &str) -> Option<&'static str> {
let path = path.strip_prefix("std/").unwrap_or(path);
self.prompt_assets
.iter()
.find_map(|entry| (entry.path == path).then_some(entry.source))
}
pub fn cli_script(&self, name: &str) -> Option<&'static str> {
self.cli_scripts
.iter()
.find_map(|entry| (entry.name == name).then_some(entry.source))
}
}
pub type LazyCatalog = LazyLock<StdlibCatalog>;
static INSTALLED: OnceLock<&'static LazyCatalog> = OnceLock::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CatalogNotInstalled;
impl std::fmt::Display for CatalogNotInstalled {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(
"no Harn standard library is installed: the host must link harn-stdlib and call \
harn_stdlib::install() before running Harn code",
)
}
}
impl std::error::Error for CatalogNotInstalled {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CatalogAlreadyInstalled;
impl std::fmt::Display for CatalogAlreadyInstalled {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a different Harn standard library is already installed")
}
}
impl std::error::Error for CatalogAlreadyInstalled {}
pub fn install(catalog: &'static LazyCatalog) -> Result<(), CatalogAlreadyInstalled> {
let installed = INSTALLED.get_or_init(|| catalog);
if std::ptr::eq(*installed, catalog) {
Ok(())
} else {
Err(CatalogAlreadyInstalled)
}
}
pub fn ensure_installed() -> Result<(), CatalogNotInstalled> {
INSTALLED.get().map(|_| ()).ok_or(CatalogNotInstalled)
}
pub fn installed() -> Result<&'static StdlibCatalog, CatalogNotInstalled> {
INSTALLED
.get()
.map(|catalog| &***catalog)
.ok_or(CatalogNotInstalled)
}
pub fn builtin_reexports(module: &str) -> &'static [&'static str] {
match module {
"testing" => &[
"assert",
"assert_approx",
"assert_eq",
"assert_matches",
"assert_ne",
"skip",
"value_diff",
],
_ => &[],
}
}
#[cfg(test)]
mod tests {
use super::*;
static ONE: LazyCatalog = LazyCatalog::new(|| StdlibCatalog {
sources: &[StdlibSource {
module: "text",
source: "pub fn shout() {}",
}],
prompt_assets: &[StdlibPromptAsset {
path: "agent/prompts/x.harn.prompt",
source: "hello",
}],
cli_scripts: &[],
});
static OTHER: LazyCatalog = LazyCatalog::new(|| StdlibCatalog {
sources: &[],
prompt_assets: &[],
cli_scripts: &[],
});
#[test]
fn lookups_answer_by_module_and_by_prefixed_path() {
assert_eq!(ONE.source("text"), Some("pub fn shout() {}"));
assert_eq!(ONE.source("missing"), None);
assert_eq!(
ONE.prompt_asset("std/agent/prompts/x.harn.prompt"),
Some("hello")
);
}
static BUILDS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
static COUNTED: LazyCatalog = LazyCatalog::new(|| {
BUILDS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
StdlibCatalog {
sources: &[],
prompt_assets: &[],
cli_scripts: &[],
}
});
#[test]
fn a_reader_before_install_gets_a_typed_error_then_the_one_catalog() {
assert_eq!(installed().err(), Some(CatalogNotInstalled));
assert_eq!(ensure_installed(), Err(CatalogNotInstalled));
install(&COUNTED).expect("first install");
install(&COUNTED).expect("same catalog again");
assert_eq!(install(&OTHER), Err(CatalogAlreadyInstalled));
ensure_installed().expect("installed");
assert_eq!(BUILDS.load(std::sync::atomic::Ordering::SeqCst), 0);
let counted: &StdlibCatalog = &COUNTED;
assert!(std::ptr::eq(installed().expect("installed"), counted));
assert_eq!(BUILDS.load(std::sync::atomic::Ordering::SeqCst), 1);
}
}