use sim_cookbook::RecipeCard;
use sim_kernel::{CapabilityName, Cx, GrantSeat, Lib, Result, Symbol};
pub trait LibCatalog {
fn resolve(&self, name: &str) -> Option<&dyn Lib>;
}
pub struct EmptyCatalog;
impl LibCatalog for EmptyCatalog {
fn resolve(&self, _name: &str) -> Option<&dyn Lib> {
None
}
}
pub fn load_requires(cx: &mut Cx, catalog: &dyn LibCatalog, card: &RecipeCard) -> Vec<String> {
let mut unresolved = Vec::new();
for req in &card.requires {
if lib_present(cx, req) {
continue;
}
match catalog.resolve(req) {
Some(lib) => {
let mut visiting = Vec::new();
if let Err(err) = load_lib_with_deps(cx, catalog, lib, &mut visiting) {
unresolved.push(format!("{req} (load failed: {err})"));
}
}
None => unresolved.push(req.clone()),
}
}
unresolved
}
fn lib_present(cx: &Cx, name: &str) -> bool {
cx.registry().libs().iter().any(|lib| {
lib.manifest.id.as_qualified_str() == name || lib.manifest.id.name.as_ref() == name
})
}
fn load_lib_with_deps(
cx: &mut Cx,
catalog: &dyn LibCatalog,
lib: &dyn Lib,
visiting: &mut Vec<Symbol>,
) -> Result<()> {
let manifest = lib.manifest();
if cx.registry().lib(&manifest.id).is_some() || visiting.contains(&manifest.id) {
return Ok(());
}
visiting.push(manifest.id.clone());
for dep in &manifest.requires {
let resolved = catalog
.resolve(&dep.id.as_qualified_str())
.or_else(|| catalog.resolve(dep.id.name.as_ref()));
if let Some(dep_lib) = resolved {
load_lib_with_deps(cx, catalog, dep_lib, visiting)?;
}
}
cx.load_lib(lib)?;
visiting.pop();
Ok(())
}
#[derive(Clone, Debug, Default)]
pub struct CookbookCapabilityProfile;
impl CookbookCapabilityProfile {
pub fn granted() -> Vec<CapabilityName> {
[
"read-construct",
"read-eval",
"compute",
"codec-encode",
"codec-decode",
"offline-render",
"cassette-replay",
"model-fixture",
]
.into_iter()
.map(CapabilityName::new)
.collect()
}
pub fn denied() -> Vec<CapabilityName> {
[
"net-connect",
"device-open",
"process-spawn",
"clock-now",
"fs-write",
"rng-unseeded",
]
.into_iter()
.map(CapabilityName::new)
.collect()
}
pub fn grants(capability: &CapabilityName) -> bool {
Self::granted().contains(capability)
}
pub fn denies(capability: &CapabilityName) -> bool {
Self::denied().contains(capability)
}
pub fn seat(seat: &GrantSeat, cx: &mut Cx) -> Result<()> {
for capability in Self::granted() {
seat.grant(cx, capability);
}
Ok(())
}
}