use std::sync::{Arc, Mutex};
use sim_cookbook::RecipeStore;
use sim_kernel::{
AbiVersion, CORE_TABLE_CLASS_ID, ClassRef, Cx, Export, Lib, LibManifest, LibTarget, Linker,
LoadCx, Object, ObjectCompat, Result, Symbol, Value, Version,
};
use crate::cli::{cookbook_cli_exports, register_cookbook_cli};
use crate::ops::{CookbookOp, OpKind};
pub fn manifest_name() -> Symbol {
Symbol::qualified("sim", "cookbook")
}
pub fn store_symbol() -> Symbol {
Symbol::qualified("cookbook", "store")
}
#[sim_citizen_derive::non_citizen(
reason = "live cookbook store handle; reconstruct from embedded recipe descriptors",
kind = "handle",
descriptor = "cookbook/Recipe"
)]
#[derive(Clone)]
pub struct CookbookStoreHandle {
store: Arc<Mutex<RecipeStore>>,
}
impl CookbookStoreHandle {
pub fn new(store: Arc<Mutex<RecipeStore>>) -> Self {
Self { store }
}
pub fn store(&self) -> Arc<Mutex<RecipeStore>> {
self.store.clone()
}
}
impl Object for CookbookStoreHandle {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok("#<cookbook-store>".to_owned())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ObjectCompat for CookbookStoreHandle {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
cx.factory()
.class_stub(CORE_TABLE_CLASS_ID, Symbol::qualified("core", "Table"))
}
fn as_table(&self, cx: &mut Cx) -> Result<Value> {
let count = self.store.lock().expect("recipe store lock").len();
let kind = cx
.factory()
.symbol(Symbol::qualified("cookbook", "store"))?;
let recipes = cx.factory().string(count.to_string())?;
cx.factory().table(vec![
(Symbol::new("kind"), kind),
(Symbol::new("recipes"), recipes),
])
}
}
pub struct CookbookLib {
store: Arc<Mutex<RecipeStore>>,
}
impl CookbookLib {
pub fn new(store: RecipeStore) -> Self {
Self {
store: Arc::new(Mutex::new(store)),
}
}
pub fn store(&self) -> Arc<Mutex<RecipeStore>> {
self.store.clone()
}
}
impl Lib for CookbookLib {
fn manifest(&self) -> LibManifest {
LibManifest {
id: manifest_name(),
version: Version(env!("CARGO_PKG_VERSION").to_owned()),
abi: AbiVersion { major: 0, minor: 1 },
target: LibTarget::HostRegistered,
requires: Vec::new(),
capabilities: Vec::new(),
exports: {
let mut exports = op_exports();
exports.extend(cookbook_cli_exports());
exports
},
}
}
fn load(&self, cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
for kind in OpKind::ALL {
let op = CookbookOp::new(self.store.clone(), kind);
linker.function_value(kind.symbol(), cx.factory().opaque(Arc::new(op))?)?;
}
let store = CookbookStoreHandle::new(self.store.clone());
linker.value(store_symbol(), cx.factory().opaque(Arc::new(store))?)?;
register_cookbook_cli(cx, linker)?;
Ok(())
}
}
pub fn op_exports() -> Vec<Export> {
let mut exports = OpKind::ALL
.into_iter()
.map(|kind| Export::Function {
symbol: kind.symbol(),
function_id: None,
})
.collect::<Vec<_>>();
exports.push(Export::Value {
symbol: store_symbol(),
});
exports
}
pub fn install_cookbook_lib(cx: &mut Cx, store: RecipeStore) -> Result<()> {
sim_lib_core::install_once(cx, &CookbookLib::new(store))?;
Ok(())
}