sim_lib_cookbook/
runtime.rs1use std::sync::{Arc, Mutex};
8
9use sim_cookbook::RecipeStore;
10use sim_kernel::{
11 AbiVersion, CORE_TABLE_CLASS_ID, ClassRef, Cx, Export, Lib, LibManifest, LibTarget, Linker,
12 LoadCx, Object, ObjectCompat, Result, Symbol, Value, Version,
13};
14
15use crate::cli::{cookbook_cli_exports, register_cookbook_cli};
16use crate::ops::{CookbookOp, OpKind};
17
18pub fn manifest_name() -> Symbol {
20 Symbol::qualified("sim", "cookbook")
21}
22
23pub fn store_symbol() -> Symbol {
25 Symbol::qualified("cookbook", "store")
26}
27
28#[sim_citizen_derive::non_citizen(
30 reason = "live cookbook store handle; reconstruct from embedded recipe descriptors",
31 kind = "handle",
32 descriptor = "cookbook/Recipe"
33)]
34#[derive(Clone)]
35pub struct CookbookStoreHandle {
36 store: Arc<Mutex<RecipeStore>>,
37}
38
39impl CookbookStoreHandle {
40 pub fn new(store: Arc<Mutex<RecipeStore>>) -> Self {
42 Self { store }
43 }
44
45 pub fn store(&self) -> Arc<Mutex<RecipeStore>> {
47 self.store.clone()
48 }
49}
50
51impl Object for CookbookStoreHandle {
52 fn display(&self, _cx: &mut Cx) -> Result<String> {
53 Ok("#<cookbook-store>".to_owned())
54 }
55
56 fn as_any(&self) -> &dyn std::any::Any {
57 self
58 }
59}
60
61impl ObjectCompat for CookbookStoreHandle {
62 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
63 cx.factory()
64 .class_stub(CORE_TABLE_CLASS_ID, Symbol::qualified("core", "Table"))
65 }
66
67 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
68 let count = self.store.lock().expect("recipe store lock").len();
69 let kind = cx
70 .factory()
71 .symbol(Symbol::qualified("cookbook", "store"))?;
72 let recipes = cx.factory().string(count.to_string())?;
73 cx.factory().table(vec![
74 (Symbol::new("kind"), kind),
75 (Symbol::new("recipes"), recipes),
76 ])
77 }
78}
79
80pub struct CookbookLib {
82 store: Arc<Mutex<RecipeStore>>,
83}
84
85impl CookbookLib {
86 pub fn new(store: RecipeStore) -> Self {
88 Self {
89 store: Arc::new(Mutex::new(store)),
90 }
91 }
92
93 pub fn store(&self) -> Arc<Mutex<RecipeStore>> {
95 self.store.clone()
96 }
97}
98
99impl Lib for CookbookLib {
100 fn manifest(&self) -> LibManifest {
101 LibManifest {
102 id: manifest_name(),
103 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
104 abi: AbiVersion { major: 0, minor: 1 },
105 target: LibTarget::HostRegistered,
106 requires: Vec::new(),
107 capabilities: Vec::new(),
108 exports: {
109 let mut exports = op_exports();
110 exports.extend(cookbook_cli_exports());
111 exports
112 },
113 }
114 }
115
116 fn load(&self, cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
117 for kind in OpKind::ALL {
118 let op = CookbookOp::new(self.store.clone(), kind);
119 linker.function_value(kind.symbol(), cx.factory().opaque(Arc::new(op))?)?;
120 }
121 let store = CookbookStoreHandle::new(self.store.clone());
122 linker.value(store_symbol(), cx.factory().opaque(Arc::new(store))?)?;
123 register_cookbook_cli(cx, linker)?;
124 Ok(())
125 }
126}
127
128pub fn op_exports() -> Vec<Export> {
130 let mut exports = OpKind::ALL
131 .into_iter()
132 .map(|kind| Export::Function {
133 symbol: kind.symbol(),
134 function_id: None,
135 })
136 .collect::<Vec<_>>();
137 exports.push(Export::Value {
138 symbol: store_symbol(),
139 });
140 exports
141}
142
143pub fn install_cookbook_lib(cx: &mut Cx, store: RecipeStore) -> Result<()> {
145 sim_lib_core::install_once(cx, &CookbookLib::new(store))?;
146 Ok(())
147}