sim_kernel/library/registry/
load.rs1use std::cmp::Ordering;
2use std::collections::{BTreeMap, BTreeSet};
3
4use crate::{
5 error::{Error, Result},
6 id::LibId,
7 library::{Export, ExportRecord, LibManifest, LoadTransaction, Version},
8};
9
10use super::Registry;
11use super::commit::commit_loaded_lib;
12use crate::library::loaders::compare_version_text;
13use crate::library::transaction::PendingExports;
14
15impl Registry {
16 pub fn dependency_order(&self, manifests: &[LibManifest]) -> Result<Vec<LibManifest>> {
25 let mut remaining = manifests.to_vec();
26 remaining.sort_by(|left, right| left.id.cmp(&right.id));
27 let mut loaded = self.libs_by_symbol.keys().cloned().collect::<BTreeSet<_>>();
28 let mut loaded_versions = self
29 .libs
30 .iter()
31 .map(|loaded| (loaded.manifest.id.clone(), loaded.manifest.version.clone()))
32 .collect::<BTreeMap<_, _>>();
33 let mut ordered = Vec::with_capacity(remaining.len());
34
35 while !remaining.is_empty() {
36 let mut progressed = false;
37 let mut index = 0;
38 while index < remaining.len() {
39 let ready = remaining[index].requires.iter().all(|dependency| {
40 if !loaded.contains(&dependency.id) {
41 return false;
42 }
43 match (
44 loaded_versions.get(&dependency.id),
45 dependency.minimum_version.as_ref(),
46 ) {
47 (Some(loaded_version), Some(required_version)) => {
48 compare_version_text(&loaded_version.0, &required_version.0)
49 != Ordering::Less
50 }
51 _ => true,
52 }
53 });
54 if ready {
55 let manifest = remaining.remove(index);
56 loaded.insert(manifest.id.clone());
57 loaded_versions.insert(manifest.id.clone(), manifest.version.clone());
58 ordered.push(manifest);
59 progressed = true;
60 } else {
61 index += 1;
62 }
63 }
64
65 if !progressed {
66 let blocked = &remaining[0];
67 if let Some(dependency) = blocked.requires.iter().find(|dependency| {
68 loaded_versions
69 .get(&dependency.id)
70 .zip(dependency.minimum_version.as_ref())
71 .is_some_and(|(loaded_version, minimum)| {
72 compare_version_text(&loaded_version.0, &minimum.0) == Ordering::Less
73 })
74 }) {
75 return Err(Error::DependencyVersionMismatch {
76 lib: blocked.id.clone(),
77 dependency: dependency.id.clone(),
78 required: dependency
79 .minimum_version
80 .clone()
81 .unwrap_or_else(|| Version(String::from("0"))),
82 loaded: loaded_versions
83 .get(&dependency.id)
84 .cloned()
85 .unwrap_or_else(|| Version(String::from("0"))),
86 });
87 }
88 let missing = blocked
89 .requires
90 .iter()
91 .find(|dependency| !loaded.contains(&dependency.id))
92 .map(|dependency| dependency.id.clone())
93 .unwrap_or_else(|| blocked.id.clone());
94 return Err(if missing == blocked.id {
95 Error::CyclicDependency {
96 symbol: blocked.id.clone(),
97 }
98 } else {
99 Error::MissingDependency {
100 lib: blocked.id.clone(),
101 dependency: missing,
102 }
103 });
104 }
105 }
106
107 Ok(ordered)
108 }
109
110 pub fn begin_load(&self, manifest: LibManifest, trusted: bool) -> LoadTransaction {
154 self.try_begin_load(manifest, trusted)
155 .unwrap_or_else(|err| panic!("{err}"))
156 }
157
158 pub fn try_begin_load(&self, manifest: LibManifest, trusted: bool) -> Result<LoadTransaction> {
160 let mut registry = self.clone();
161 let lib_id = registry.try_fresh_lib_id()?;
162 Ok(LoadTransaction {
163 lib_id,
164 manifest,
165 trusted,
166 registry,
167 pending: PendingExports::default(),
168 stable_exports: BTreeMap::new(),
169 })
170 }
171
172 pub fn commit_load(&mut self, txn: LoadTransaction) -> Result<LibId> {
175 let lib_id = txn.lib_id;
176 let mut registry = txn.registry;
177 let sequence_before = self.catalog_sequence_snapshot();
178 commit_loaded_lib(
179 txn.lib_id,
180 &mut registry,
181 txn.manifest,
182 txn.trusted,
183 txn.pending,
184 sequence_before,
185 )?;
186 *self = registry;
187 Ok(lib_id)
188 }
189
190 pub(crate) fn ensure_export_available(&self, export: &Export) -> Result<()> {
191 let duplicate = self
192 .export_symbols
193 .get(&export.kind_symbol())
194 .is_some_and(|entries| entries.contains_key(export.symbol()));
195 if duplicate {
196 Err(Error::DuplicateExport {
197 kind: export.kind(),
198 symbol: export.symbol().clone(),
199 })
200 } else {
201 Ok(())
202 }
203 }
204
205 pub(crate) fn validate_export_record_against_manifest(
206 manifest: &LibManifest,
207 record: &ExportRecord,
208 ) -> Result<()> {
209 let declared = manifest
210 .exports
211 .iter()
212 .any(|export| export.kind_symbol() == record.kind && export.symbol() == &record.symbol);
213 if declared {
214 Ok(())
215 } else {
216 Err(Error::UndeclaredExportRecord {
217 kind: record.kind.clone(),
218 symbol: record.symbol.clone(),
219 })
220 }
221 }
222}