1use std::cmp::Ordering;
2use std::sync::Arc;
3
4use crate::{
5 Datum,
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 try_fresh_class_id(&mut self) -> Result<ClassId> {
61 self.registry.try_fresh_class_id()
62 }
63
64 pub fn fresh_function_id(&mut self) -> FunctionId {
66 self.registry.fresh_function_id()
67 }
68
69 pub fn try_fresh_function_id(&mut self) -> Result<FunctionId> {
71 self.registry.try_fresh_function_id()
72 }
73
74 pub fn fresh_macro_id(&mut self) -> MacroId {
76 self.registry.fresh_macro_id()
77 }
78
79 pub fn try_fresh_macro_id(&mut self) -> Result<MacroId> {
81 self.registry.try_fresh_macro_id()
82 }
83
84 pub fn fresh_case_id(&mut self) -> CaseId {
86 self.registry.fresh_case_id()
87 }
88
89 pub fn try_fresh_case_id(&mut self) -> Result<CaseId> {
91 self.registry.try_fresh_case_id()
92 }
93
94 pub fn fresh_shape_id(&mut self) -> ShapeId {
96 self.registry.fresh_shape_id()
97 }
98
99 pub fn try_fresh_shape_id(&mut self) -> Result<ShapeId> {
101 self.registry.try_fresh_shape_id()
102 }
103
104 pub fn fresh_codec_id(&mut self) -> CodecId {
106 self.registry.fresh_codec_id()
107 }
108
109 pub fn try_fresh_codec_id(&mut self) -> Result<CodecId> {
111 self.registry.try_fresh_codec_id()
112 }
113
114 pub fn fresh_number_domain_id(&mut self) -> NumberDomainId {
116 self.registry.fresh_number_domain_id()
117 }
118
119 pub fn try_fresh_number_domain_id(&mut self) -> Result<NumberDomainId> {
122 self.registry.try_fresh_number_domain_id()
123 }
124
125 pub fn require(&self, capability: &CapabilityName) -> Result<()> {
129 if self.capabilities.contains(capability) {
130 Ok(())
131 } else {
132 Err(Error::CapabilityDenied {
133 capability: capability.clone(),
134 })
135 }
136 }
137
138 pub fn resolve_class(&self, symbol: &Symbol) -> Result<Value> {
140 self.registry
141 .class_by_symbol(symbol)
142 .cloned()
143 .ok_or_else(|| Error::UnknownClass {
144 class: symbol.clone(),
145 })
146 }
147
148 pub fn resolve_function(&self, symbol: &Symbol) -> Result<Value> {
150 self.registry
151 .function_by_symbol(symbol)
152 .cloned()
153 .ok_or_else(|| Error::UnknownFunction {
154 function: symbol.clone(),
155 })
156 }
157
158 pub fn resolve_macro(&self, symbol: &Symbol) -> Result<Value> {
160 self.registry
161 .macro_by_symbol(symbol)
162 .cloned()
163 .ok_or_else(|| Error::UnknownSymbol {
164 symbol: symbol.clone(),
165 })
166 }
167
168 pub fn resolve_shape(&self, symbol: &Symbol) -> Result<Value> {
170 self.registry
171 .shape_by_symbol(symbol)
172 .cloned()
173 .ok_or_else(|| Error::UnknownSymbol {
174 symbol: symbol.clone(),
175 })
176 }
177
178 pub fn resolve_codec(&self, symbol: &Symbol) -> Result<Value> {
180 self.registry
181 .codec_by_symbol(symbol)
182 .cloned()
183 .ok_or_else(|| Error::UnknownSymbol {
184 symbol: symbol.clone(),
185 })
186 }
187
188 pub fn resolve_number_domain(&self, symbol: &Symbol) -> Result<Value> {
190 self.registry
191 .number_domain_by_symbol(symbol)
192 .cloned()
193 .ok_or_else(|| Error::UnknownSymbol {
194 symbol: symbol.clone(),
195 })
196 }
197
198 pub fn resolve_value(&self, symbol: &Symbol) -> Result<Value> {
200 self.registry
201 .value_by_symbol(symbol)
202 .cloned()
203 .ok_or_else(|| Error::UnknownSymbol {
204 symbol: symbol.clone(),
205 })
206 }
207}
208
209fn trim_trailing_numeric_zero_components<'a>(components: &'a [&'a str]) -> &'a [&'a str] {
210 let mut end = components.len();
211 while end > 0 && components[end - 1].parse::<u64>() == Ok(0) {
212 end -= 1;
213 }
214 &components[..end]
215}
216
217pub(crate) fn compare_version_text(left: &str, right: &str) -> Ordering {
218 let left_components = left.split('.').collect::<Vec<_>>();
219 let right_components = right.split('.').collect::<Vec<_>>();
220 let left = trim_trailing_numeric_zero_components(&left_components);
221 let right = trim_trailing_numeric_zero_components(&right_components);
222 let len = left.len().max(right.len());
223 for index in 0..len {
224 let left_component = left.get(index).copied().unwrap_or("0");
225 let right_component = right.get(index).copied().unwrap_or("0");
226 let ordering = match (
227 left_component.parse::<u64>(),
228 right_component.parse::<u64>(),
229 ) {
230 (Ok(left_number), Ok(right_number)) => left_number.cmp(&right_number),
231 _ => left_component.cmp(right_component),
232 };
233 if ordering != Ordering::Equal {
234 return ordering;
235 }
236 }
237 Ordering::Equal
238}
239
240pub(crate) fn dependency_satisfied(loaded: &LibManifest, dependency: &Dependency) -> bool {
241 match &dependency.minimum_version {
242 Some(minimum) => compare_version_text(&loaded.version.0, &minimum.0) != Ordering::Less,
243 None => true,
244 }
245}
246
247pub trait Lib {
253 fn manifest(&self) -> LibManifest;
255 fn load(&self, cx: &mut LoadCx, linker: &mut Linker) -> Result<()>;
257
258 fn unload(&self, _cx: &mut Cx, _linker: &mut Linker) -> Result<()> {
267 Ok(())
268 }
269}
270
271pub enum LibSource {
273 Symbol(Symbol),
275 Open {
277 kind: Symbol,
279 payload: Datum,
281 },
282 Host(Box<dyn Lib>),
284}
285
286impl LibSource {
287 pub fn open(kind: Symbol, payload: Datum) -> Self {
289 Self::Open { kind, payload }
290 }
291}
292
293#[derive(Clone, Debug, PartialEq, Eq)]
299pub enum CatalogSource {
300 Open {
302 kind: Symbol,
304 payload: Datum,
306 },
307}
308
309impl CatalogSource {
310 pub fn open(kind: Symbol, payload: Datum) -> Self {
312 Self::Open { kind, payload }
313 }
314}
315
316impl From<CatalogSource> for LibSource {
317 fn from(source: CatalogSource) -> Self {
318 match source {
319 CatalogSource::Open { kind, payload } => Self::Open { kind, payload },
320 }
321 }
322}
323
324pub trait LibLoader: Send + Sync {
329 fn can_load(&self, source: &LibSource) -> bool;
331 fn load(&self, cx: &mut Cx, source: LibSource) -> Result<Box<dyn Lib>>;
333
334 fn inspect_manifest(&self, _cx: &mut Cx, _source: &LibSource) -> Result<Option<LibManifest>> {
337 Ok(None)
338 }
339}
340
341#[derive(Default)]
343pub struct LoaderRegistry {
344 loaders: Vec<Box<dyn LibLoader>>,
345 sources: std::collections::BTreeMap<Symbol, CatalogSource>,
346}
347
348impl LoaderRegistry {
349 pub fn new() -> Self {
351 Self::default()
352 }
353
354 pub fn with_loader(mut self, loader: impl LibLoader + 'static) -> Self {
356 self.loaders.push(Box::new(loader));
357 self
358 }
359
360 pub fn add_loader(&mut self, loader: impl LibLoader + 'static) {
362 self.loaders.push(Box::new(loader));
363 }
364
365 pub fn with_source(mut self, symbol: Symbol, source: CatalogSource) -> Self {
367 self.sources.insert(symbol, source);
368 self
369 }
370
371 pub fn add_source(&mut self, symbol: Symbol, source: CatalogSource) {
373 self.sources.insert(symbol, source);
374 }
375
376 pub fn load_lib(&self, cx: &mut Cx, source: LibSource) -> Result<Box<dyn Lib>> {
379 if let LibSource::Symbol(symbol) = &source
380 && let Some(resolved) = self.sources.get(symbol).cloned()
381 {
382 return self.load_lib(cx, resolved.into());
383 }
384 for loader in &self.loaders {
385 if loader.can_load(&source) {
386 return loader.load(cx, source);
387 }
388 }
389 match source {
390 LibSource::Symbol(symbol) => Err(Error::HostError(format!(
391 "no loader accepted lib source symbol {}",
392 symbol
393 ))),
394 LibSource::Open { kind, .. } => Err(Error::HostError(format!(
395 "no loader accepted lib source kind {}",
396 kind
397 ))),
398 _ => Err(Error::HostError("no loader accepted lib source".to_owned())),
399 }
400 }
401
402 pub fn load_and_register(&self, cx: &mut Cx, source: LibSource) -> Result<crate::LibId> {
404 let lib = self.load_lib(cx, source)?;
405 cx.load_lib(lib.as_ref())
406 }
407
408 pub fn resolve_source_spec(&self, source: &LibSourceSpec) -> LibSourceSpec {
410 match source {
411 LibSourceSpec::Symbol(symbol) => self
412 .sources
413 .get(symbol)
414 .cloned()
415 .map(LibSourceSpec::from)
416 .unwrap_or_else(|| source.clone()),
417 _ => source.clone(),
418 }
419 }
420
421 pub fn load_and_register_with_receipt(
424 &self,
425 cx: &mut Cx,
426 source: LibSourceSpec,
427 ) -> Result<LibBootReceipt> {
428 let resolved = self.resolve_source_spec(&source);
429 self.load_and_register_from_specs(cx, source, resolved)
430 }
431
432 pub fn replay_boot_receipts(
435 &self,
436 cx: &mut Cx,
437 receipts: &[LibBootReceipt],
438 ) -> Result<Vec<LibBootReceipt>> {
439 receipts
440 .iter()
441 .map(|expected| {
442 let actual = self.load_and_register_from_specs(
443 cx,
444 expected.requested_source.clone(),
445 expected.resolved_source.clone(),
446 )?;
447 if &actual == expected {
448 Ok(actual)
449 } else {
450 Err(Error::Lib(format!(
451 "boot receipt replay mismatch for {}",
452 expected.manifest.id
453 )))
454 }
455 })
456 .collect()
457 }
458
459 pub fn replay_boot_state(
462 &self,
463 cx: &mut Cx,
464 state: &RegistryBootState,
465 ) -> Result<Vec<LibBootReceipt>> {
466 self.replay_boot_receipts(cx, &state.receipts)
467 }
468
469 pub fn inspect_manifest(&self, cx: &mut Cx, source: LibSource) -> Result<LibManifest> {
472 if let LibSource::Symbol(symbol) = &source
473 && let Some(resolved) = self.sources.get(symbol).cloned()
474 {
475 return self.inspect_manifest(cx, resolved.into());
476 }
477 for loader in &self.loaders {
478 if loader.can_load(&source) {
479 if let Some(manifest) = loader.inspect_manifest(cx, &source)? {
480 return Ok(manifest);
481 }
482 return Err(Error::HostError(
483 "loader does not support manifest inspection before load".to_owned(),
484 ));
485 }
486 }
487 match source {
488 LibSource::Symbol(symbol) => Err(Error::HostError(format!(
489 "no loader accepted lib source symbol {}",
490 symbol
491 ))),
492 LibSource::Open { kind, .. } => Err(Error::HostError(format!(
493 "no loader accepted lib source kind {} for inspection",
494 kind
495 ))),
496 _ => Err(Error::HostError(
497 "no loader accepted lib source for inspection".to_owned(),
498 )),
499 }
500 }
501
502 fn load_and_register_from_specs(
503 &self,
504 cx: &mut Cx,
505 requested: LibSourceSpec,
506 resolved: LibSourceSpec,
507 ) -> Result<LibBootReceipt> {
508 let lib = self.load_lib(cx, resolved.clone().into())?;
509 let lib_id = cx.load_lib(lib.as_ref())?;
510 cx.registry()
511 .boot_receipt(lib_id, requested, resolved)
512 .ok_or_else(|| Error::Lib(format!("loaded lib id {lib_id:?} is not registered")))
513 }
514}