use sim_cookbook::RecipeCard;
use sim_kernel::{CapabilityName, Cx, GrantSeat, Lib, Result};
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 {
let already_loaded = cx.registry().libs().iter().any(|lib| {
lib.manifest.id.as_qualified_str() == *req
|| lib.manifest.id.name.as_ref() == req.as_str()
});
if already_loaded {
continue;
}
match catalog.resolve(req) {
Some(lib) if cx.registry().lib(&lib.manifest().id).is_none() => {
if let Err(err) = cx.load_lib(lib) {
unresolved.push(format!("{req} (load failed: {err})"));
}
}
Some(_) => {}
None => unresolved.push(req.clone()),
}
}
unresolved
}
#[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(())
}
}