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 {
150 self.try_begin_load(manifest, trusted)
151 .unwrap_or_else(|err| panic!("{err}"))
152 }
153
154 pub fn try_begin_load(&self, manifest: LibManifest, trusted: bool) -> Result<LoadTransaction> {
156 let mut registry = self.clone();
157 let lib_id = registry.try_fresh_lib_id()?;
158 Ok(LoadTransaction {
159 lib_id,
160 manifest,
161 trusted,
162 registry,
163 pending: PendingExports::default(),
164 })
165 }
166
167 pub fn commit_load(&mut self, txn: LoadTransaction) -> Result<LibId> {
170 let lib_id = txn.lib_id;
171 let mut registry = txn.registry;
172 let sequence_before = self.catalog_sequence_snapshot();
173 commit_loaded_lib(
174 txn.lib_id,
175 &mut registry,
176 txn.manifest,
177 txn.trusted,
178 txn.pending,
179 sequence_before,
180 )?;
181 *self = registry;
182 Ok(lib_id)
183 }
184
185 pub(crate) fn ensure_export_available(&self, export: &Export) -> Result<()> {
186 let duplicate = self
187 .export_symbols
188 .get(&export.kind_symbol())
189 .is_some_and(|entries| entries.contains_key(export.symbol()));
190 if duplicate {
191 Err(Error::DuplicateExport {
192 kind: export.kind(),
193 symbol: export.symbol().clone(),
194 })
195 } else {
196 Ok(())
197 }
198 }
199
200 pub(crate) fn validate_export_record_against_manifest(
201 manifest: &LibManifest,
202 record: &ExportRecord,
203 ) -> Result<()> {
204 let declared = manifest
205 .exports
206 .iter()
207 .any(|export| export.kind_symbol() == record.kind && export.symbol() == &record.symbol);
208 if declared {
209 Ok(())
210 } else {
211 Err(Error::UndeclaredExportRecord {
212 kind: record.kind.clone(),
213 symbol: record.symbol.clone(),
214 })
215 }
216 }
217}