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::loadable::{LifecycleAction, LoadableLibList};
17use crate::ops::{CookbookLifecycleOp, CookbookOp, OpKind};
18
19pub fn manifest_name() -> Symbol {
21 Symbol::qualified("sim", "cookbook")
22}
23
24pub fn store_symbol() -> Symbol {
26 Symbol::qualified("cookbook", "store")
27}
28
29#[sim_citizen_derive::non_citizen(
31 reason = "live cookbook store handle; reconstruct from embedded recipe descriptors",
32 kind = "handle",
33 descriptor = "cookbook/Recipe"
34)]
35#[derive(Clone)]
36pub struct CookbookStoreHandle {
37 store: Arc<Mutex<RecipeStore>>,
38}
39
40impl CookbookStoreHandle {
41 pub fn new(store: Arc<Mutex<RecipeStore>>) -> Self {
43 Self { store }
44 }
45
46 pub fn store(&self) -> Arc<Mutex<RecipeStore>> {
48 self.store.clone()
49 }
50}
51
52impl Object for CookbookStoreHandle {
53 fn display(&self, _cx: &mut Cx) -> Result<String> {
54 Ok("#<cookbook-store>".to_owned())
55 }
56
57 fn as_any(&self) -> &dyn std::any::Any {
58 self
59 }
60}
61
62impl ObjectCompat for CookbookStoreHandle {
63 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
64 cx.factory()
65 .class_stub(CORE_TABLE_CLASS_ID, Symbol::qualified("core", "Table"))
66 }
67
68 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
69 let count = self.store.lock().expect("recipe store lock").len();
70 let kind = cx
71 .factory()
72 .symbol(Symbol::qualified("cookbook", "store"))?;
73 let recipes = cx.factory().string(count.to_string())?;
74 cx.factory().table(vec![
75 (Symbol::new("kind"), kind),
76 (Symbol::new("recipes"), recipes),
77 ])
78 }
79}
80
81pub struct CookbookLib {
83 store: Arc<Mutex<RecipeStore>>,
84 loadable_libs: Arc<LoadableLibList>,
85}
86
87impl CookbookLib {
88 pub fn new(store: RecipeStore) -> Self {
90 Self {
91 store: Arc::new(Mutex::new(store)),
92 loadable_libs: Arc::new(LoadableLibList::new(Vec::new())),
93 }
94 }
95
96 pub fn with_loadable_libs(store: RecipeStore, loadable_libs: LoadableLibList) -> Self {
98 Self {
99 store: Arc::new(Mutex::new(store)),
100 loadable_libs: Arc::new(loadable_libs),
101 }
102 }
103
104 pub fn store(&self) -> Arc<Mutex<RecipeStore>> {
106 self.store.clone()
107 }
108}
109
110impl Lib for CookbookLib {
111 fn manifest(&self) -> LibManifest {
112 LibManifest {
113 id: manifest_name(),
114 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
115 abi: AbiVersion { major: 0, minor: 1 },
116 target: LibTarget::HostRegistered,
117 requires: Vec::new(),
118 capabilities: Vec::new(),
119 exports: {
120 let mut exports = op_exports();
121 exports.extend(cookbook_cli_exports());
122 exports
123 },
124 }
125 }
126
127 fn load(&self, cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
128 for kind in OpKind::ALL {
129 let op = CookbookOp::new(self.store.clone(), kind);
130 linker.function_value(kind.symbol(), cx.factory().opaque(Arc::new(op))?)?;
131 }
132 for action in [LifecycleAction::Load, LifecycleAction::Unload] {
133 let op = CookbookLifecycleOp::new(self.loadable_libs.clone(), action);
134 linker.function_value(
135 CookbookLifecycleOp::symbol(action),
136 cx.factory().opaque(Arc::new(op))?,
137 )?;
138 }
139 let store = CookbookStoreHandle::new(self.store.clone());
140 linker.value(store_symbol(), cx.factory().opaque(Arc::new(store))?)?;
141 register_cookbook_cli(cx, linker)?;
142 Ok(())
143 }
144}
145
146pub fn op_exports() -> Vec<Export> {
148 let mut exports = OpKind::ALL
149 .into_iter()
150 .map(|kind| Export::Function {
151 symbol: kind.symbol(),
152 function_id: None,
153 })
154 .collect::<Vec<_>>();
155 exports.extend(
156 [LifecycleAction::Load, LifecycleAction::Unload]
157 .into_iter()
158 .map(|action| Export::Function {
159 symbol: CookbookLifecycleOp::symbol(action),
160 function_id: None,
161 }),
162 );
163 exports.push(Export::Value {
164 symbol: store_symbol(),
165 });
166 exports
167}
168
169pub fn install_cookbook_lib(cx: &mut Cx, store: RecipeStore) -> Result<()> {
171 sim_lib_core::install_once(cx, &CookbookLib::new(store))?;
172 Ok(())
173}
174
175pub fn install_cookbook_lib_with_loadable_libs(
177 cx: &mut Cx,
178 store: RecipeStore,
179 loadable_libs: LoadableLibList,
180) -> Result<()> {
181 sim_lib_core::install_once(cx, &CookbookLib::with_loadable_libs(store, loadable_libs))?;
182 Ok(())
183}