use std::sync::Arc;
use sim_kernel::{
AbiVersion, Cx, Export, Lib, LibId, LibManifest, LibTarget, Linker, Ref, Result, Symbol,
Version,
};
use crate::{
FidelityBadge, LanguageProfile, LanguageProfileValue, ProfileRegistry,
publish_profile_claims_for_lib,
};
pub type ProfileOrganPublisher = fn(&mut Cx, LibId) -> Result<()>;
pub fn fidelity_badge(profile: &Symbol, badge: Symbol, level: u8, test: &Symbol) -> FidelityBadge {
FidelityBadge::new(
Ref::Symbol(profile.clone()),
badge,
level,
Ref::Symbol(test.clone()),
)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FidelityBadgeSpec {
pub badge: Symbol,
pub level: u8,
pub test: Symbol,
}
impl FidelityBadgeSpec {
pub fn into_badge(self, profile: &Symbol) -> FidelityBadge {
fidelity_badge(profile, self.badge, self.level, &self.test)
}
}
pub fn install_language_profile(
cx: &mut Cx,
registry: &mut ProfileRegistry,
profile: LanguageProfile,
organ_publishers: &[ProfileOrganPublisher],
) -> Result<LanguageProfile> {
let lib_id = ensure_profile_lib(cx, &profile)?;
registry.register_profile(profile.clone())?;
publish_profile_claims_for_lib(cx, lib_id, &profile)?;
for publish in organ_publishers {
publish(cx, lib_id)?;
}
Ok(profile)
}
pub fn language_profile_lib_symbol(profile: &Symbol) -> Symbol {
Symbol::qualified("standard/profile", profile.to_string())
}
#[derive(Clone)]
struct LanguageProfileLib {
profile: LanguageProfile,
}
impl LanguageProfileLib {
fn new(profile: LanguageProfile) -> Self {
Self { profile }
}
}
impl Lib for LanguageProfileLib {
fn manifest(&self) -> LibManifest {
LibManifest {
id: language_profile_lib_symbol(&self.profile.symbol),
version: Version(env!("CARGO_PKG_VERSION").to_owned()),
abi: AbiVersion { major: 0, minor: 1 },
target: LibTarget::HostRegistered,
requires: Vec::new(),
capabilities: Vec::new(),
exports: vec![Export::Value {
symbol: self.profile.symbol.clone(),
}],
}
}
fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
linker.value(
self.profile.symbol.clone(),
cx.factory()
.opaque(Arc::new(LanguageProfileValue::new(self.profile.clone())))?,
)
}
}
fn ensure_profile_lib(cx: &mut Cx, profile: &LanguageProfile) -> Result<LibId> {
let lib = LanguageProfileLib::new(profile.clone());
let manifest = lib.manifest();
if let Some(loaded) = cx.registry().lib(&manifest.id) {
return Ok(loaded.id);
}
cx.load_lib(&lib)
}
#[cfg(test)]
mod tests {
use super::*;
fn profile_symbol() -> Symbol {
Symbol::qualified("test", "profile")
}
fn test_symbol() -> Symbol {
Symbol::qualified("test", "conformance")
}
fn badge_symbol() -> Symbol {
Symbol::qualified("test", "badge")
}
#[test]
fn fidelity_badge_matches_manual_construction() {
let profile = profile_symbol();
let test = test_symbol();
let helper = fidelity_badge(&profile, badge_symbol(), 1, &test);
let manual = FidelityBadge::new(
Ref::Symbol(profile.clone()),
badge_symbol(),
1,
Ref::Symbol(test.clone()),
);
assert_eq!(helper, manual);
}
#[test]
fn spec_into_badge_matches_fidelity_badge() {
let profile = profile_symbol();
let spec = FidelityBadgeSpec {
badge: badge_symbol(),
level: 2,
test: test_symbol(),
};
let from_spec = spec.into_badge(&profile);
let direct = fidelity_badge(&profile, badge_symbol(), 2, &test_symbol());
assert_eq!(from_spec, direct);
}
#[test]
fn install_language_profile_registers_and_runs_publishers_in_order() {
use std::cell::RefCell;
use std::sync::Arc;
use sim_kernel::{DefaultFactory, NoopEvalPolicy};
thread_local! {
static CALLS: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };
}
fn first(_cx: &mut Cx, _lib_id: LibId) -> Result<()> {
CALLS.with(|calls| calls.borrow_mut().push(1));
Ok(())
}
fn second(_cx: &mut Cx, _lib_id: LibId) -> Result<()> {
CALLS.with(|calls| calls.borrow_mut().push(2));
Ok(())
}
CALLS.with(|calls| calls.borrow_mut().clear());
let mut cx = Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory));
let mut registry = ProfileRegistry::new();
let profile = LanguageProfile::new(profile_symbol());
let installed =
install_language_profile(&mut cx, &mut registry, profile.clone(), &[first, second])
.expect("install language profile");
assert_eq!(installed, profile);
assert!(registry.profile(&profile_symbol()).is_some());
CALLS.with(|calls| assert_eq!(*calls.borrow(), vec![1, 2]));
}
}