1use alloc::{boxed::Box, collections::BTreeMap, format, string::ToString, sync::Arc, vec::Vec};
2use std::{
3 fs,
4 path::{Path as FsPath, PathBuf},
5};
6
7use miden_assembly_syntax::{ast::ModuleKind, diagnostics::Report};
8use miden_mast_package::{Package as MastPackage, TargetType};
9use miden_package_registry::{PackageCache, PackageId, Version as PackageVersion};
10use miden_project::{
11 Linkage, Package as ProjectPackage, PreassembledDependencyMetadata, Profile,
12 ProjectDependencyNodeProvenance, ProjectSource, ProjectSourceOrigin, Target,
13};
14
15use crate::{Assembler, ast::Module};
16
17mod build_provenance;
18mod dependency_graph;
19mod providers;
20mod runtime_dependencies;
21mod target_selector;
22
23use self::{
24 build_provenance::PackageBuildProvenance, dependency_graph::DependencyGraph,
25 runtime_dependencies::RuntimeDependencies,
26};
27pub use self::{
28 providers::{MasmSourceProvider, ProjectSourceProvider, TargetAssemblyContext},
29 target_selector::ProjectTargetSelector,
30};
31
32#[cfg(test)]
33mod tests;
34
35impl Assembler {
39 pub fn for_project_at_path<'a, S>(
41 self,
42 manifest_path: impl AsRef<FsPath>,
43 store: &'a mut S,
44 ) -> Result<ProjectAssembler<'a, S>, Report>
45 where
46 S: PackageCache + ?Sized,
47 {
48 let masm_provider = Box::new(MasmSourceProvider) as Box<_>;
49 self.for_project_at_path_with_providers(manifest_path, store, [masm_provider])
50 }
51
52 pub fn for_project_at_path_with_providers<'a, S>(
54 self,
55 manifest_path: impl AsRef<FsPath>,
56 store: &'a mut S,
57 providers: impl IntoIterator<Item = Box<dyn ProjectSourceProvider>>,
58 ) -> Result<ProjectAssembler<'a, S>, Report>
59 where
60 S: PackageCache + ?Sized,
61 {
62 let manifest_path = manifest_path.as_ref();
63 let source_manager = self.source_manager();
64 let project = miden_project::Project::load(manifest_path, &source_manager)?;
65 let package = project.package();
66 let dependency_graph =
67 DependencyGraph::from_project_path(manifest_path, store, source_manager)?;
68
69 Ok(ProjectAssembler {
70 assembler: self,
71 project: package,
72 source_provider: SourceProviderRegistry::new(providers),
73 dependency_graph,
74 store,
75 })
76 }
77
78 pub fn for_project<'a, S>(
80 self,
81 project: Arc<ProjectPackage>,
82 store: &'a mut S,
83 ) -> Result<ProjectAssembler<'a, S>, Report>
84 where
85 S: PackageCache + ?Sized,
86 {
87 let masm_provider = Box::new(MasmSourceProvider) as Box<_>;
88 self.for_project_with_providers(project, store, [masm_provider])
89 }
90
91 pub fn for_project_with_providers<'a, S>(
93 self,
94 project: Arc<ProjectPackage>,
95 store: &'a mut S,
96 providers: impl IntoIterator<Item = Box<dyn ProjectSourceProvider>>,
97 ) -> Result<ProjectAssembler<'a, S>, Report>
98 where
99 S: PackageCache + ?Sized,
100 {
101 let source_manager = self.source_manager();
102 let dependency_graph =
103 DependencyGraph::from_project(project.clone(), store, source_manager)?;
104 Ok(ProjectAssembler {
105 assembler: self,
106 project,
107 source_provider: SourceProviderRegistry::new(providers),
108 dependency_graph,
109 store,
110 })
111 }
112}
113
114pub struct ProjectSourceInputs {
118 pub root: Box<Module>,
119 pub support: Vec<Box<Module>>,
120}
121
122pub struct ProjectSourceProvenanceInputs {
123 pub root: SourceFileProvenance,
124 pub support: Vec<SourceFileProvenance>,
125}
126
127pub struct SourceFileProvenance {
128 pub path: Box<std::path::Path>,
129 pub content: Box<str>,
130}
131
132impl SourceFileProvenance {
133 pub fn from_path(path: PathBuf) -> Result<Self, Report> {
134 let content = fs::read_to_string(&path).map_err(|err| {
135 Report::msg(format!("unable to read source file '{}': {err}", path.display()))
136 })?;
137 Ok(Self {
138 path: path.into_boxed_path(),
139 content: content.into_boxed_str(),
140 })
141 }
142}
143
144pub struct SourceProviderRegistry {
145 registered: BTreeMap<&'static str, Box<dyn ProjectSourceProvider>>,
146}
147
148impl Default for SourceProviderRegistry {
149 fn default() -> Self {
150 Self {
151 registered: BTreeMap::from_iter([(
152 "masm",
153 Box::new(MasmSourceProvider) as Box<dyn ProjectSourceProvider>,
154 )]),
155 }
156 }
157}
158
159impl SourceProviderRegistry {
160 pub fn new(providers: impl IntoIterator<Item = Box<dyn ProjectSourceProvider>>) -> Self {
161 let mut this = Self {
162 registered: providers.into_iter().map(|p| (p.file_type(), p)).collect(),
163 };
164
165 if !this.registered.contains_key("masm") {
166 this.registered.insert("masm", Box::new(MasmSourceProvider));
167 }
168
169 this
170 }
171
172 pub fn with_source_provider(
173 &mut self,
174 provider: impl ProjectSourceProvider + 'static,
175 ) -> &mut Self {
176 let file_type = provider.file_type();
177 let provider = Box::new(provider) as Box<dyn ProjectSourceProvider>;
178
179 self.registered.insert(file_type, provider);
180
181 self
182 }
183
184 #[inline]
185 pub fn get_provider(&self, file_type: &str) -> Option<&dyn ProjectSourceProvider> {
186 self.registered.get(file_type).map(AsRef::as_ref)
187 }
188}
189
190pub struct ProjectAssembler<'a, S: PackageCache + ?Sized> {
191 assembler: Assembler,
192 project: Arc<ProjectPackage>,
193 dependency_graph: DependencyGraph,
194 source_provider: SourceProviderRegistry,
195 store: &'a mut S,
196}
197
198impl<'a, S> ProjectAssembler<'a, S>
199where
200 S: PackageCache + ?Sized,
201{
202 pub fn with_source_provider(
203 &mut self,
204 provider: impl ProjectSourceProvider + 'static,
205 ) -> &mut Self {
206 self.source_provider.with_source_provider(provider);
207 self
208 }
209
210 pub fn project(&self) -> &ProjectPackage {
211 self.project.as_ref()
212 }
213
214 pub fn assemble(
215 &mut self,
216 target_selector: ProjectTargetSelector<'_>,
217 profile_name: &str,
218 ) -> Result<Arc<MastPackage>, Report> {
219 let target = target_selector.select_target(self.project.as_ref())?;
220
221 let mut cache = BTreeMap::new();
224 let root_id = self.dependency_graph.root().clone();
225 let required_lib = if target.is_executable()
226 && let Some(library_target) =
227 self.project.library_target().map(|target| target.inner().clone())
228 {
229 Some(self.assemble_source_package(
230 root_id.clone(),
231 Arc::clone(&self.project),
232 &library_target,
233 profile_name,
234 None,
235 &mut cache,
236 )?)
237 } else {
238 None
239 };
240
241 self.assemble_source_package(
242 root_id,
243 Arc::clone(&self.project),
244 &target,
245 profile_name,
246 required_lib,
247 &mut cache,
248 )
249 .map(|resolved| resolved.package)
250 }
251
252 fn assemble_source_package(
253 &mut self,
254 package_id: PackageId,
255 project: Arc<ProjectPackage>,
256 target: &Target,
257 profile_name: &str,
258 required_lib: Option<ResolvedPackage>,
259 cache: &mut BTreeMap<PackageId, ResolvedPackage>,
260 ) -> Result<ResolvedPackage, Report> {
261 let cache_key = project.target_package_name(target);
262 if let Some(package) = cache.get(&cache_key).cloned() {
263 assert_eq!(package.package.kind, target.ty);
264 return Ok(package);
265 }
266
267 let profile = project.resolve_profile(profile_name)?;
268 let mut assembler = self
269 .assembler
270 .clone()
271 .with_emit_debug_info(profile.should_emit_debug_info())
272 .with_trim_paths(profile.should_trim_paths());
273 let mut runtime_dependencies = RuntimeDependencies::default();
274 debug_assert!(
275 required_lib.is_none() || target.ty.is_executable(),
276 "expected required_lib only for executable targets"
277 );
278 match required_lib {
279 Some(required_lib) if required_lib.package.is_kernel() => {
280 runtime_dependencies.record_linked_kernel_dependency(required_lib.package)?;
284 },
285 Some(required_lib) => {
286 assembler.link_package(required_lib.package.clone(), Linkage::Static)?;
287 if let Some(kernel_package) = required_lib.linked_kernel_package {
288 runtime_dependencies.record_linked_kernel_dependency(kernel_package)?;
289 }
290 },
291 None => (),
292 }
293
294 let node = self.dependency_graph.get(&package_id)?;
295 let dependencies = node.dependencies.clone();
296 for edge in dependencies.iter() {
297 let dependency_package =
298 self.resolve_dependency_package(&edge.dependency, profile_name, cache)?;
299 if !dependency_package.package.is_library() {
300 return Err(Report::msg(format!(
301 "dependency '{}' resolved to executable package '{}', but only library-like packages can be linked",
302 edge.dependency, dependency_package.package.name
303 )));
304 }
305
306 if !dependency_package.package.is_kernel() {
307 assembler.link_package(dependency_package.package.clone(), edge.linkage)?;
308 }
309 runtime_dependencies.merge_package(dependency_package, edge.linkage)?;
310 }
311
312 let ProjectSourceInputs { root, support } =
313 self.load_target_sources(project.as_ref(), target, profile)?;
314
315 let mut sections = Vec::new();
317
318 if let Some(provenance) = self.dependency_graph.build_source_provenance(
322 &package_id,
323 project.as_ref(),
324 target,
325 profile_name,
326 &self.source_provider,
327 )? {
328 sections.push(provenance.to_section());
329 }
330
331 if let Some(kernel_package) = runtime_dependencies.kernel.clone() {
332 if matches!(target.ty, TargetType::Kernel) {
333 return Err(Report::msg(format!(
334 "kernel targets cannot depend on a kernel, dependency '{}' is a kernel",
335 kernel_package.name
336 )));
337 }
338 assembler.link_package(kernel_package, Linkage::Dynamic)?;
339 }
340
341 let mut product = match target.ty {
342 TargetType::Executable => {
343 assembler.assemble_executable_modules(package_id.clone(), root, support)?
344 },
345 _ if target.ty.is_library() => {
346 assembler.assemble_library_modules(package_id.clone(), root, support, target.ty)?
347 },
348 _ => unreachable!("non-exhaustive target type"),
349 };
350
351 product
352 .extend_dependencies(runtime_dependencies.deps.into_values())
353 .expect("assembled package manifest should have unique runtime dependencies");
354
355 let mut package = product.into_artifact()?;
356 package.name = project.target_package_name(target);
357 package.version = project.version().into_inner().clone();
358 package.description = project.description().map(|description| description.to_string());
359 package.sections.extend(sections);
360 let package = Arc::from(package);
361
362 let resolved = ResolvedPackage {
363 package,
364 linked_kernel_package: runtime_dependencies.kernel,
365 };
366 cache.insert(package_id, resolved.clone());
367
368 Ok(resolved)
369 }
370
371 fn resolve_dependency_package(
372 &mut self,
373 package_id: &PackageId,
374 profile_name: &str,
375 cache: &mut BTreeMap<PackageId, ResolvedPackage>,
376 ) -> Result<ResolvedPackage, Report> {
377 if let Some(package) = cache.get(package_id).cloned() {
378 return Ok(package);
379 }
380
381 let node = self.dependency_graph.get(package_id)?;
382 let node_version = node.version.clone();
383
384 let (package, should_cache) = match &node.provenance {
385 ProjectDependencyNodeProvenance::Source(ProjectSource::Virtual { .. }) => {
386 return Err(Report::msg(format!(
387 "package '{package_id}' is missing a manifest path",
388 )));
389 },
390 ProjectDependencyNodeProvenance::Source(ProjectSource::Real {
391 manifest_path,
392 origin,
393 library_path: Some(_),
394 ..
395 }) => {
396 let project = miden_project::Project::load_project_reference(
397 package_id,
398 manifest_path,
399 &self.assembler.source_manager(),
400 )
401 .map(|project| project.package())?;
402 let target = project
403 .library_target()
404 .map(|target| target.inner().clone())
405 .ok_or_else(|| {
406 Report::msg(format!(
407 "dependency '{package_id}' does not define a library target"
408 ))
409 })?;
410 match self.try_reuse_registered_source_package(
411 package_id,
412 &node_version,
413 &project,
414 &target,
415 profile_name,
416 origin,
417 manifest_path,
418 )? {
419 RegisteredSourcePackage::Loaded(package) => (
420 ResolvedPackage {
421 linked_kernel_package: self
422 .resolve_linked_kernel_package(package.clone())?,
423 package,
424 },
425 false,
426 ),
427 reuse => {
428 let package = self.assemble_source_package(
429 package_id.clone(),
430 project,
431 &target,
432 profile_name,
433 None,
434 cache,
435 )?;
436 match reuse {
437 RegisteredSourcePackage::Missing => (),
438 RegisteredSourcePackage::IndexedButUnreadable(expected) => {
439 let actual = PackageVersion::new(
440 package.package.version.clone(),
441 package.package.digest(),
442 );
443 if actual != expected {
444 return Err(Report::msg(format!(
445 "package '{package_id}' version '{node_version}' is already registered as '{expected}', but the canonical artifact could not be loaded and rebuilding from source produced '{actual}'; bump the semantic version or repair the package store"
446 )));
447 }
448 },
449 RegisteredSourcePackage::Loaded(_) => unreachable!(),
450 }
451 (package, true)
452 },
453 }
454 },
455 ProjectDependencyNodeProvenance::Source(_) => {
456 let package =
457 self.load_canonical_package(package_id, &node_version)?.ok_or_else(|| {
458 Report::msg(format!(
459 "dependency '{package_id}' version '{node_version}' was not found in the package registry"
460 ))
461 })?;
462 (
463 ResolvedPackage {
464 linked_kernel_package: self
465 .resolve_linked_kernel_package(package.clone())?,
466 package,
467 },
468 false,
469 )
470 },
471 ProjectDependencyNodeProvenance::Registry { selected, .. } => {
472 let package = self.store.load_package(package_id, selected)?;
473 (
474 ResolvedPackage {
475 linked_kernel_package: self
476 .resolve_linked_kernel_package(package.clone())?,
477 package,
478 },
479 false,
480 )
481 },
482 ProjectDependencyNodeProvenance::Preassembled {
483 path,
484 selected,
485 kind,
486 requirements,
487 } => {
488 let package = load_selected_preassembled_package(
489 path,
490 package_id,
491 selected,
492 *kind,
493 requirements,
494 )?;
495 let should_cache = self.should_cache_preassembled_package(package_id, selected);
496 (
497 ResolvedPackage {
498 linked_kernel_package: self
499 .resolve_linked_kernel_package(package.clone())?,
500 package,
501 },
502 should_cache,
503 )
504 },
505 };
506
507 if should_cache {
508 self.cache_resolved_package(&package)?;
509 }
510 cache.insert(package_id.clone(), package.clone());
511 Ok(package)
512 }
513
514 fn resolve_linked_kernel_package(
515 &self,
516 package: Arc<MastPackage>,
517 ) -> Result<Option<Arc<MastPackage>>, Report> {
518 if package.is_kernel() {
519 return Ok(Some(package));
520 }
521
522 let Some(kernel_dependency) = package.kernel_runtime_dependency()? else {
523 return Ok(None);
524 };
525
526 let version =
527 PackageVersion::new(kernel_dependency.version.clone(), kernel_dependency.digest);
528 if self.store.get_exact_version(&kernel_dependency.name, &version).is_some() {
529 match self.store.load_package(&kernel_dependency.name, &version) {
530 Ok(kernel_package) => {
531 if !kernel_package.is_kernel() {
532 return Err(Report::msg(format!(
533 "runtime kernel dependency '{}@{}#{}' resolved to non-kernel package '{}'",
534 kernel_dependency.name,
535 kernel_dependency.version,
536 kernel_dependency.digest,
537 kernel_package.name
538 )));
539 }
540 return Ok(Some(kernel_package));
541 },
542 Err(load_error) => {
543 if let Some(kernel_package) = package
544 .try_embedded_kernel_package()
545 .map(|kernel_package| kernel_package.map(Arc::from))?
546 {
547 return Ok(Some(kernel_package));
548 }
549 return Err(load_error);
550 },
551 }
552 }
553
554 package
555 .try_embedded_kernel_package()
556 .map(|kernel_package| kernel_package.map(Arc::from))
557 }
558
559 fn load_canonical_package(
560 &self,
561 package_id: &PackageId,
562 version: &miden_project::SemVer,
563 ) -> Result<Option<Arc<MastPackage>>, Report> {
564 let Some(record) = self.store.get_by_semver(package_id, version) else {
565 return Ok(None);
566 };
567 self.store.load_package(package_id, record.version()).map(Some)
568 }
569
570 fn try_reuse_registered_source_package(
571 &self,
572 package_id: &PackageId,
573 version: &miden_project::SemVer,
574 project: &ProjectPackage,
575 target: &Target,
576 profile_name: &str,
577 origin: &ProjectSourceOrigin,
578 manifest_path: &FsPath,
579 ) -> Result<RegisteredSourcePackage, Report> {
580 let Some(record) = self.store.get_by_semver(package_id, version) else {
581 return Ok(RegisteredSourcePackage::Missing);
582 };
583 let package = match self.store.load_package(package_id, record.version()) {
584 Ok(package) => package,
585 Err(_) => {
586 return Ok(RegisteredSourcePackage::IndexedButUnreadable(record.version().clone()));
587 },
588 };
589
590 let expected = self.dependency_graph.expected_source_provenance(
591 package_id,
592 project,
593 target,
594 profile_name,
595 origin,
596 manifest_path,
597 &self.source_provider,
598 )?;
599
600 match PackageBuildProvenance::from_package(&package)? {
601 Some(actual) if actual == expected => Ok(()),
602 Some(actual) => Err(Report::msg(format!(
603 "package '{}' version '{}' is already registered with different source provenance (expected {}, found {}); bump the semantic version",
604 package_id,
605 version,
606 expected.describe(),
607 actual.describe(),
608 ))),
609 None => Err(Report::msg(format!(
610 "package '{package_id}' version '{version}' is already registered, but the canonical artifact is missing source provenance; bump the semantic version"
611 ))),
612 }?;
613
614 Ok(RegisteredSourcePackage::Loaded(package))
615 }
616
617 fn should_cache_preassembled_package(
618 &self,
619 package_id: &PackageId,
620 selected: &PackageVersion,
621 ) -> bool {
622 let Some(record) = self.store.get_by_semver(package_id, &selected.version) else {
623 return true;
624 };
625 if record.version() != selected {
626 return false;
627 }
628
629 self.store.load_package(package_id, selected).is_err()
630 }
631
632 fn cache_resolved_package(&mut self, package: &ResolvedPackage) -> Result<(), Report> {
633 self.cache_package(package.package.clone())?;
634 if let Some(kernel_package) = package.linked_kernel_package.clone()
635 && self.should_cache_linked_kernel_package(kernel_package.as_ref())
636 {
637 self.cache_package(kernel_package)?;
638 }
639 Ok(())
640 }
641
642 fn should_cache_linked_kernel_package(&self, package: &MastPackage) -> bool {
643 let version = PackageVersion::new(package.version.clone(), package.digest());
644 let Some(record) = self.store.get_by_semver(&package.name, &package.version) else {
645 return true;
646 };
647 if record.version() != &version {
648 return false;
649 }
650
651 self.store.load_package(&package.name, &version).is_err()
652 }
653
654 fn cache_package(&mut self, package: Arc<MastPackage>) -> Result<(), Report> {
655 self.store
656 .cache_package(package)
657 .map(|_| ())
658 .map_err(|error| Report::msg(error.to_string()))
659 }
660
661 fn load_target_sources(
662 &self,
663 project: &ProjectPackage,
664 target: &Target,
665 profile: &Profile,
666 ) -> Result<ProjectSourceInputs, Report> {
667 let manifest_path = project.expect_manifest_path()?;
668 let mut context = TargetAssemblyContext::new(
669 project,
670 manifest_path,
671 target,
672 profile,
673 self.dependency_graph.as_ref(),
674 self.assembler.source_manager(),
675 )?;
676 context.with_warnings_as_errors(self.assembler.warnings_as_errors());
677
678 let extension = context.resolved_target_root.extension().ok_or_else(|| {
679 Report::msg(format!(
680 "invalid target 'path' {}: path must have an extension",
681 context.resolved_target_root.display()
682 ))
683 })?;
684 let extension = extension.to_string_lossy();
685
686 let provider = self.source_provider.get_provider(extension.as_ref()).ok_or_else(|| Report::msg(format!("unsupported target file type '{extension}': no provider has been registered for that file type")))?;
687 let inputs = provider.provide_sources(&context)?;
688 match target.ty {
689 TargetType::Executable if !inputs.root.kind().is_executable() => {
690 Err(Report::msg(format!(
691 "requested target type is executable, but root module provided to assembler for '{}' is {}",
692 project.name(),
693 inputs.root.kind()
694 )))
695 },
696 TargetType::Kernel if !inputs.root.kind().is_kernel() => Err(Report::msg(format!(
697 "requested target type is kernel, but root module provided to assembler for '{}' is {}",
698 project.name(),
699 inputs.root.kind()
700 ))),
701 _ if inputs.root.path() != target.namespace.inner().as_ref() => {
702 Err(Report::msg(format!(
703 "requested target namespace is '{}', but root module provided to assembler for '{}' is '{}'",
704 &target.namespace,
705 project.name(),
706 inputs.root.path()
707 )))
708 },
709 _ => Ok(inputs),
710 }
711 }
712}
713
714#[derive(Clone)]
717struct ResolvedPackage {
718 package: Arc<MastPackage>,
719 linked_kernel_package: Option<Arc<MastPackage>>,
720}
721
722enum RegisteredSourcePackage {
723 Missing,
724 Loaded(Arc<MastPackage>),
725 IndexedButUnreadable(PackageVersion),
726}
727
728#[derive(Debug, Clone, PartialEq, Eq)]
729struct PackageBuildSettings {
730 emit_debug_info: bool,
731 trim_paths: bool,
732}
733
734impl PackageBuildSettings {
735 fn legacy() -> Self {
736 Self { emit_debug_info: true, trim_paths: false }
737 }
738
739 fn from_profile(profile: &Profile) -> Self {
740 Self {
741 emit_debug_info: profile.should_emit_debug_info(),
742 trim_paths: profile.should_trim_paths(),
743 }
744 }
745
746 fn is_legacy(&self) -> bool {
747 *self == Self::legacy()
748 }
749}
750
751fn load_selected_preassembled_package(
755 path: &FsPath,
756 expected_name: &PackageId,
757 selected: &PackageVersion,
758 expected_kind: TargetType,
759 expected_requirements: &BTreeMap<PackageId, PreassembledDependencyMetadata>,
760) -> Result<Arc<MastPackage>, Report> {
761 let package = load_package_from_path(path)?;
762 if &package.name != expected_name {
763 return Err(Report::msg(format!(
764 "preassembled dependency '{}' at '{}' resolved to package '{}'",
765 expected_name,
766 path.display(),
767 package.name
768 )));
769 }
770
771 let actual = PackageVersion::new(package.version.clone(), package.digest());
772 if &actual != selected {
773 return Err(Report::msg(format!(
774 "preassembled dependency '{}@{}' at '{}' no longer matches the dependency graph selection '{}'",
775 expected_name,
776 actual,
777 path.display(),
778 selected
779 )));
780 }
781
782 if package.kind != expected_kind {
783 return Err(Report::msg(format!(
784 "preassembled dependency '{}@{}' at '{}' no longer matches the dependency graph target kind '{}'",
785 expected_name,
786 actual,
787 path.display(),
788 expected_kind
789 )));
790 }
791
792 let actual_requirements = package_requirements(&package);
793 if &actual_requirements != expected_requirements {
794 return Err(Report::msg(format!(
795 "preassembled dependency '{}@{}' at '{}' no longer matches the dependency graph dependency requirements",
796 expected_name,
797 actual,
798 path.display()
799 )));
800 }
801
802 Ok(package)
803}
804
805fn load_package_from_path(path: &FsPath) -> Result<Arc<MastPackage>, Report> {
806 let bytes = fs::read(path)
807 .map_err(|error| Report::msg(format!("failed to read '{}': {error}", path.display())))?;
808 let package = MastPackage::read_from_bytes_trusted(&bytes).map_err(|error| {
809 Report::msg(format!("failed to decode package '{}': {error}", path.display()))
810 })?;
811 Ok(Arc::new(package))
812}
813
814fn package_requirements(
815 package: &MastPackage,
816) -> BTreeMap<PackageId, PreassembledDependencyMetadata> {
817 package
818 .manifest
819 .dependencies()
820 .map(|dependency| {
821 (
822 dependency.name.clone(),
823 PreassembledDependencyMetadata {
824 version: PackageVersion::new(dependency.version.clone(), dependency.digest),
825 kind: dependency.kind,
826 },
827 )
828 })
829 .collect()
830}