use crate::{
LibId,
env::Cx,
error::{Error, Result},
library::{Lib, LibTarget, Version},
};
use super::claims::publish_loaded_lib_claims;
use super::loaders::dependency_satisfied;
use std::collections::BTreeMap;
impl Cx {
pub fn activate_lib(&mut self, expected: Option<LibId>, lib: &dyn Lib) -> Result<LibId> {
let manifest = lib.manifest();
let current = self.registry.lib(&manifest.id).cloned();
match (expected, current.as_ref()) {
(None, None) => {}
(None, Some(_)) => {
return Err(Error::DuplicateLib {
symbol: manifest.id.clone(),
});
}
(Some(id), Some(loaded)) if loaded.id == id => {}
(Some(id), _) => {
return Err(Error::Lib(format!(
"activation expected library id {id:?} for {}",
manifest.id
)));
}
}
if expected.is_none() {
let mut staged = self.library_state();
let id = self.with_library_state(&mut staged, |cx| cx.load_lib(lib))?;
self.commit_library_state(staged);
return Ok(id);
}
let loaded = current.expect("validated loaded replacement");
let id = loaded.id;
let stable_exports = loaded
.exports
.iter()
.filter_map(|record| match record.state {
crate::library::ExportState::Resolved { id } => {
Some(((record.kind.clone(), record.symbol.clone()), id))
}
_ => None,
})
.collect::<BTreeMap<_, _>>();
let mut staged = self.library_state();
let result = self.with_library_state(&mut staged, |cx| {
for capability in &manifest.capabilities {
cx.require(capability)?;
}
for dependency in &manifest.requires {
if let Some(found) = cx.registry.manifest_by_symbol(&dependency.id)
&& !dependency_satisfied(found, dependency)
{
return Err(Error::DependencyVersionMismatch {
lib: manifest.id.clone(),
dependency: dependency.id.clone(),
required: dependency
.minimum_version
.clone()
.unwrap_or_else(|| Version(String::from("0"))),
loaded: found.version.clone(),
});
}
}
let sequence_floor = cx.registry.catalog_sequence_snapshot();
cx.registry.unload(id)?;
cx.remove_load_claims(&[id]);
cx.registry.retain_sequence_floor(&sequence_floor)?;
let trusted = matches!(manifest.target, LibTarget::HostRegistered);
let mut txn = cx.registry.try_begin_load(manifest.clone(), trusted)?;
txn.lib_id = id;
txn.stable_exports = stable_exports;
{
let mut load_cx = cx.load_cx();
lib.load(&mut load_cx, &mut txn.linker())?;
}
let committed = cx.registry.commit_load(txn)?;
let loaded = cx
.registry
.libs()
.iter()
.find(|item| item.id == committed)
.cloned()
.ok_or_else(|| Error::Lib("activated library missing after commit".to_owned()))?;
let claims = publish_loaded_lib_claims(cx, &loaded)?;
cx.record_load_claims(committed, claims);
Ok(committed)
})?;
self.commit_library_state(staged);
Ok(result)
}
pub fn load_lib(&mut self, lib: &dyn Lib) -> Result<LibId> {
let manifest = lib.manifest();
for capability in &manifest.capabilities {
self.require(capability)?;
}
for dependency in &manifest.requires {
if let Some(loaded_manifest) = self.registry.manifest_by_symbol(&dependency.id)
&& !dependency_satisfied(loaded_manifest, dependency)
{
return Err(Error::DependencyVersionMismatch {
lib: manifest.id.clone(),
dependency: dependency.id.clone(),
required: dependency
.minimum_version
.clone()
.unwrap_or_else(|| Version(String::from("0"))),
loaded: loaded_manifest.version.clone(),
});
}
}
let trusted = matches!(manifest.target, LibTarget::HostRegistered);
let mut txn = self.registry.try_begin_load(manifest, trusted)?;
{
let mut load_cx = self.load_cx();
let mut linker = txn.linker();
lib.load(&mut load_cx, &mut linker)?;
}
let lib_id = self.registry.commit_load(txn)?;
let loaded = self
.registry
.libs()
.iter()
.find(|loaded| loaded.id == lib_id)
.cloned();
if let Some(loaded) = loaded {
let claim_ids = publish_loaded_lib_claims(self, &loaded)?;
self.record_load_claims(lib_id, claim_ids);
}
Ok(lib_id)
}
pub fn load_libs(&mut self, libs: &[&dyn Lib]) -> Result<Vec<LibId>> {
let manifests = libs.iter().map(|lib| lib.manifest()).collect::<Vec<_>>();
let ordered = self.registry.dependency_order(&manifests)?;
let mut ids = Vec::with_capacity(ordered.len());
for manifest in ordered {
let lib = libs
.iter()
.find(|candidate| candidate.manifest().id == manifest.id)
.ok_or_else(|| {
Error::Lib(format!("missing lib implementation for {}", manifest.id))
})?;
ids.push(self.load_lib(*lib)?);
}
Ok(ids)
}
pub fn unload_lib(&mut self, lib_id: LibId) -> Result<Vec<LibId>> {
let unloaded = self.registry.unload(lib_id)?;
self.remove_load_claims(&unloaded);
Ok(unloaded)
}
pub fn unload_lib_cascade(&mut self, lib_id: LibId) -> Result<Vec<LibId>> {
let unloaded = self.registry.unload_cascade(lib_id)?;
self.remove_load_claims(&unloaded);
Ok(unloaded)
}
}