1use std::cmp::Ordering;
2use std::path::PathBuf;
3use std::sync::Arc;
4
5use crate::{
6 capability::{CapabilityName, CapabilitySet},
7 env::Cx,
8 error::{Error, Result},
9 factory::Factory,
10 id::{CaseId, ClassId, CodecId, FunctionId, MacroId, NumberDomainId, ShapeId, Symbol},
11 library::{
12 Dependency, LibBootReceipt, LibManifest, LibSourceSpec, Registry, RegistryBootState,
13 },
14 value::Value,
15};
16
17use super::transaction::Linker;
18
19pub struct LoadCx {
26 capabilities: CapabilitySet,
27 factory: Arc<dyn Factory>,
28 registry: Registry,
29}
30
31impl LoadCx {
32 pub(crate) fn new(
33 capabilities: CapabilitySet,
34 factory: Arc<dyn Factory>,
35 registry: Registry,
36 ) -> Self {
37 Self {
38 capabilities,
39 factory,
40 registry,
41 }
42 }
43
44 pub fn factory(&self) -> &dyn Factory {
46 self.factory.as_ref()
47 }
48
49 pub fn registry(&self) -> &Registry {
51 &self.registry
52 }
53
54 pub fn fresh_class_id(&mut self) -> ClassId {
56 self.registry.fresh_class_id()
57 }
58
59 pub fn fresh_function_id(&mut self) -> FunctionId {
61 self.registry.fresh_function_id()
62 }
63
64 pub fn fresh_macro_id(&mut self) -> MacroId {
66 self.registry.fresh_macro_id()
67 }
68
69 pub fn fresh_case_id(&mut self) -> CaseId {
71 self.registry.fresh_case_id()
72 }
73
74 pub fn fresh_shape_id(&mut self) -> ShapeId {
76 self.registry.fresh_shape_id()
77 }
78
79 pub fn fresh_codec_id(&mut self) -> CodecId {
81 self.registry.fresh_codec_id()
82 }
83
84 pub fn fresh_number_domain_id(&mut self) -> NumberDomainId {
86 self.registry.fresh_number_domain_id()
87 }
88
89 pub fn require(&self, capability: &CapabilityName) -> Result<()> {
93 if self.capabilities.contains(capability) {
94 Ok(())
95 } else {
96 Err(Error::CapabilityDenied {
97 capability: capability.clone(),
98 })
99 }
100 }
101
102 pub fn resolve_class(&self, symbol: &Symbol) -> Result<Value> {
104 self.registry
105 .class_by_symbol(symbol)
106 .cloned()
107 .ok_or_else(|| Error::UnknownClass {
108 class: symbol.clone(),
109 })
110 }
111
112 pub fn resolve_function(&self, symbol: &Symbol) -> Result<Value> {
114 self.registry
115 .function_by_symbol(symbol)
116 .cloned()
117 .ok_or_else(|| Error::UnknownFunction {
118 function: symbol.clone(),
119 })
120 }
121
122 pub fn resolve_macro(&self, symbol: &Symbol) -> Result<Value> {
124 self.registry
125 .macro_by_symbol(symbol)
126 .cloned()
127 .ok_or_else(|| Error::UnknownSymbol {
128 symbol: symbol.clone(),
129 })
130 }
131
132 pub fn resolve_shape(&self, symbol: &Symbol) -> Result<Value> {
134 self.registry
135 .shape_by_symbol(symbol)
136 .cloned()
137 .ok_or_else(|| Error::UnknownSymbol {
138 symbol: symbol.clone(),
139 })
140 }
141
142 pub fn resolve_codec(&self, symbol: &Symbol) -> Result<Value> {
144 self.registry
145 .codec_by_symbol(symbol)
146 .cloned()
147 .ok_or_else(|| Error::UnknownSymbol {
148 symbol: symbol.clone(),
149 })
150 }
151
152 pub fn resolve_number_domain(&self, symbol: &Symbol) -> Result<Value> {
154 self.registry
155 .number_domain_by_symbol(symbol)
156 .cloned()
157 .ok_or_else(|| Error::UnknownSymbol {
158 symbol: symbol.clone(),
159 })
160 }
161
162 pub fn resolve_value(&self, symbol: &Symbol) -> Result<Value> {
164 self.registry
165 .value_by_symbol(symbol)
166 .cloned()
167 .ok_or_else(|| Error::UnknownSymbol {
168 symbol: symbol.clone(),
169 })
170 }
171}
172
173fn trim_trailing_numeric_zero_components<'a>(components: &'a [&'a str]) -> &'a [&'a str] {
174 let mut end = components.len();
175 while end > 0 && components[end - 1].parse::<u64>() == Ok(0) {
176 end -= 1;
177 }
178 &components[..end]
179}
180
181pub(crate) fn compare_version_text(left: &str, right: &str) -> Ordering {
182 let left_components = left.split('.').collect::<Vec<_>>();
183 let right_components = right.split('.').collect::<Vec<_>>();
184 let left = trim_trailing_numeric_zero_components(&left_components);
185 let right = trim_trailing_numeric_zero_components(&right_components);
186 let len = left.len().max(right.len());
187 for index in 0..len {
188 let left_component = left.get(index).copied().unwrap_or("0");
189 let right_component = right.get(index).copied().unwrap_or("0");
190 let ordering = match (
191 left_component.parse::<u64>(),
192 right_component.parse::<u64>(),
193 ) {
194 (Ok(left_number), Ok(right_number)) => left_number.cmp(&right_number),
195 _ => left_component.cmp(right_component),
196 };
197 if ordering != Ordering::Equal {
198 return ordering;
199 }
200 }
201 Ordering::Equal
202}
203
204pub(crate) fn dependency_satisfied(loaded: &LibManifest, dependency: &Dependency) -> bool {
205 match &dependency.minimum_version {
206 Some(minimum) => compare_version_text(&loaded.version.0, &minimum.0) != Ordering::Less,
207 None => true,
208 }
209}
210
211pub trait Lib {
217 fn manifest(&self) -> LibManifest;
219 fn load(&self, cx: &mut LoadCx, linker: &mut Linker) -> Result<()>;
221
222 fn unload(&self, _cx: &mut Cx, _linker: &mut Linker) -> Result<()> {
231 Ok(())
232 }
233}
234
235pub enum LibSource {
237 Symbol(Symbol),
239 Path(PathBuf),
241 Url(String),
243 Bytes(Vec<u8>),
245 Host(Box<dyn Lib>),
247}
248
249#[derive(Clone, Debug, PartialEq, Eq)]
255pub enum CatalogSource {
256 Path(PathBuf),
258 Url(String),
260 Bytes(Vec<u8>),
262}
263
264impl From<CatalogSource> for LibSource {
265 fn from(source: CatalogSource) -> Self {
266 match source {
267 CatalogSource::Path(path) => Self::Path(path),
268 CatalogSource::Url(url) => Self::Url(url),
269 CatalogSource::Bytes(bytes) => Self::Bytes(bytes),
270 }
271 }
272}
273
274pub trait LibLoader: Send + Sync {
279 fn can_load(&self, source: &LibSource) -> bool;
281 fn load(&self, cx: &mut Cx, source: LibSource) -> Result<Box<dyn Lib>>;
283
284 fn inspect_manifest(&self, _cx: &mut Cx, _source: &LibSource) -> Result<Option<LibManifest>> {
287 Ok(None)
288 }
289}
290
291#[derive(Default)]
293pub struct LoaderRegistry {
294 loaders: Vec<Box<dyn LibLoader>>,
295 sources: std::collections::BTreeMap<Symbol, CatalogSource>,
296}
297
298impl LoaderRegistry {
299 pub fn new() -> Self {
301 Self::default()
302 }
303
304 pub fn with_loader(mut self, loader: impl LibLoader + 'static) -> Self {
306 self.loaders.push(Box::new(loader));
307 self
308 }
309
310 pub fn add_loader(&mut self, loader: impl LibLoader + 'static) {
312 self.loaders.push(Box::new(loader));
313 }
314
315 pub fn with_source(mut self, symbol: Symbol, source: CatalogSource) -> Self {
317 self.sources.insert(symbol, source);
318 self
319 }
320
321 pub fn add_source(&mut self, symbol: Symbol, source: CatalogSource) {
323 self.sources.insert(symbol, source);
324 }
325
326 pub fn load_lib(&self, cx: &mut Cx, source: LibSource) -> Result<Box<dyn Lib>> {
329 if let LibSource::Symbol(symbol) = &source
330 && let Some(resolved) = self.sources.get(symbol).cloned()
331 {
332 return self.load_lib(cx, resolved.into());
333 }
334 for loader in &self.loaders {
335 if loader.can_load(&source) {
336 return loader.load(cx, source);
337 }
338 }
339 match source {
340 LibSource::Symbol(symbol) => Err(Error::HostError(format!(
341 "no loader accepted lib source symbol {}",
342 symbol
343 ))),
344 _ => Err(Error::HostError("no loader accepted lib source".to_owned())),
345 }
346 }
347
348 pub fn load_and_register(&self, cx: &mut Cx, source: LibSource) -> Result<crate::LibId> {
350 let lib = self.load_lib(cx, source)?;
351 cx.load_lib(lib.as_ref())
352 }
353
354 pub fn resolve_source_spec(&self, source: &LibSourceSpec) -> LibSourceSpec {
356 match source {
357 LibSourceSpec::Symbol(symbol) => self
358 .sources
359 .get(symbol)
360 .cloned()
361 .map(LibSourceSpec::from)
362 .unwrap_or_else(|| source.clone()),
363 _ => source.clone(),
364 }
365 }
366
367 pub fn load_and_register_with_receipt(
370 &self,
371 cx: &mut Cx,
372 source: LibSourceSpec,
373 ) -> Result<LibBootReceipt> {
374 let resolved = self.resolve_source_spec(&source);
375 self.load_and_register_from_specs(cx, source, resolved)
376 }
377
378 pub fn replay_boot_receipts(
381 &self,
382 cx: &mut Cx,
383 receipts: &[LibBootReceipt],
384 ) -> Result<Vec<LibBootReceipt>> {
385 receipts
386 .iter()
387 .map(|expected| {
388 let actual = self.load_and_register_from_specs(
389 cx,
390 expected.requested_source.clone(),
391 expected.resolved_source.clone(),
392 )?;
393 if &actual == expected {
394 Ok(actual)
395 } else {
396 Err(Error::Lib(format!(
397 "boot receipt replay mismatch for {}",
398 expected.manifest.id
399 )))
400 }
401 })
402 .collect()
403 }
404
405 pub fn replay_boot_state(
408 &self,
409 cx: &mut Cx,
410 state: &RegistryBootState,
411 ) -> Result<Vec<LibBootReceipt>> {
412 self.replay_boot_receipts(cx, &state.receipts)
413 }
414
415 pub fn inspect_manifest(&self, cx: &mut Cx, source: LibSource) -> Result<LibManifest> {
418 if let LibSource::Symbol(symbol) = &source
419 && let Some(resolved) = self.sources.get(symbol).cloned()
420 {
421 return self.inspect_manifest(cx, resolved.into());
422 }
423 for loader in &self.loaders {
424 if loader.can_load(&source) {
425 if let Some(manifest) = loader.inspect_manifest(cx, &source)? {
426 return Ok(manifest);
427 }
428 return Err(Error::HostError(
429 "loader does not support manifest inspection before load".to_owned(),
430 ));
431 }
432 }
433 match source {
434 LibSource::Symbol(symbol) => Err(Error::HostError(format!(
435 "no loader accepted lib source symbol {}",
436 symbol
437 ))),
438 _ => Err(Error::HostError(
439 "no loader accepted lib source for inspection".to_owned(),
440 )),
441 }
442 }
443
444 fn load_and_register_from_specs(
445 &self,
446 cx: &mut Cx,
447 requested: LibSourceSpec,
448 resolved: LibSourceSpec,
449 ) -> Result<LibBootReceipt> {
450 let lib = self.load_lib(cx, resolved.clone().into())?;
451 let lib_id = cx.load_lib(lib.as_ref())?;
452 cx.registry()
453 .boot_receipt(lib_id, requested, resolved)
454 .ok_or_else(|| Error::Lib(format!("loaded lib id {lib_id:?} is not registered")))
455 }
456}