1use std::collections::HashSet;
8
9use crate::models::{
10 DatasourceId, FileInfo, Package, PackageData, PackageType, TopLevelDependency,
11};
12use strum::EnumIter;
13
14use super::{
15 AssemblerConfig, AssemblyMode, DirectoryMergeOutput, bazel_prune, clojure_deps_assign,
16 conda_rootfs_merge, debian_source_merge, file_ref_resolve, ivy_dependencies_properties_assign,
17 nix_flake_compat_merge, npm_resource_assign, nuget_cpm_resolve, python_requirements_assign,
18 resource_assign, swift_merge, topology,
19};
20
21fn merge_swift_skip(
31 _config: &AssemblerConfig,
32 _files: &[FileInfo],
33 _file_indices: &[usize],
34) -> Vec<DirectoryMergeOutput> {
35 Vec::new()
36}
37
38fn merge_cocoapods(
39 config: &AssemblerConfig,
40 files: &[FileInfo],
41 file_indices: &[usize],
42) -> Vec<DirectoryMergeOutput> {
43 super::cocoapods_merge::assemble_cocoapods_packages(config, files, file_indices)
44}
45
46fn merge_debian_source(
47 config: &AssemblerConfig,
48 files: &[FileInfo],
49 file_indices: &[usize],
50) -> Vec<DirectoryMergeOutput> {
51 debian_source_merge::assemble_debian_source_packages(config, files, file_indices)
52}
53
54fn merge_hackage(
55 _config: &AssemblerConfig,
56 files: &[FileInfo],
57 file_indices: &[usize],
58) -> Vec<DirectoryMergeOutput> {
59 super::hackage_merge::assemble_hackage_packages(files, file_indices)
60}
61
62fn merge_huggingface(
63 _config: &AssemblerConfig,
64 files: &[FileInfo],
65 file_indices: &[usize],
66) -> Vec<DirectoryMergeOutput> {
67 super::huggingface_merge::assemble_huggingface_packages(files, file_indices)
68}
69
70fn merge_windows_update(
71 config: &AssemblerConfig,
72 files: &[FileInfo],
73 file_indices: &[usize],
74) -> Vec<DirectoryMergeOutput> {
75 super::windows_update_merge::assemble_windows_update_packages(config, files, file_indices)
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
83pub(super) enum WorkspaceMarker {
84 NpmWorkspace,
85 CargoWorkspace,
86 MixUmbrella,
87 MavenReactor,
88 GradleMultiProject,
89 UvWorkspace,
90 DartWorkspace,
91}
92
93struct MarkerDetector {
98 marker: WorkspaceMarker,
99 detect: fn(DatasourceId, &PackageData) -> bool,
100}
101
102static MARKER_DETECTORS: &[MarkerDetector] = &[
103 MarkerDetector {
104 marker: WorkspaceMarker::NpmWorkspace,
105 detect: |datasource_id, data| {
106 matches!(
107 datasource_id,
108 DatasourceId::NpmPackageJson | DatasourceId::PnpmWorkspaceYaml
109 ) && data
110 .extra_data
111 .as_ref()
112 .is_some_and(|extra_data| extra_data.contains_key("workspaces"))
113 },
114 },
115 MarkerDetector {
116 marker: WorkspaceMarker::CargoWorkspace,
117 detect: |datasource_id, data| {
118 datasource_id == DatasourceId::CargoToml
119 && data
120 .extra_data
121 .as_ref()
122 .and_then(|extra_data| extra_data.get("workspace"))
123 .and_then(|workspace| workspace.get("members"))
124 .and_then(|members| members.as_array())
125 .is_some_and(|members| !members.is_empty())
126 },
127 },
128 MarkerDetector {
129 marker: WorkspaceMarker::MixUmbrella,
130 detect: |datasource_id, data| {
131 datasource_id == DatasourceId::HexMixExs
132 && data
133 .extra_data
134 .as_ref()
135 .is_some_and(|extra_data| extra_data.contains_key("apps_path"))
136 },
137 },
138 MarkerDetector {
139 marker: WorkspaceMarker::MavenReactor,
140 detect: |datasource_id, data| {
141 datasource_id == DatasourceId::MavenPom
142 && data
143 .extra_data
144 .as_ref()
145 .and_then(|extra_data| extra_data.get("modules"))
146 .and_then(|modules| modules.as_array())
147 .is_some_and(|modules| !modules.is_empty())
148 },
149 },
150 MarkerDetector {
151 marker: WorkspaceMarker::GradleMultiProject,
152 detect: |datasource_id, data| {
153 datasource_id == DatasourceId::GradleSettings
154 && data
155 .extra_data
156 .as_ref()
157 .and_then(|extra_data| extra_data.get("projects"))
158 .and_then(|projects| projects.as_array())
159 .is_some_and(|projects| !projects.is_empty())
160 },
161 },
162 MarkerDetector {
163 marker: WorkspaceMarker::UvWorkspace,
164 detect: |datasource_id, data| {
165 matches!(
166 datasource_id,
167 DatasourceId::PypiPyprojectToml | DatasourceId::PypiPoetryPyprojectToml
168 ) && data
169 .extra_data
170 .as_ref()
171 .and_then(|extra_data| extra_data.get("workspace_members"))
172 .and_then(|members| members.as_array())
173 .is_some_and(|members| !members.is_empty())
174 },
175 },
176 MarkerDetector {
177 marker: WorkspaceMarker::DartWorkspace,
178 detect: |datasource_id, data| {
179 datasource_id == DatasourceId::PubspecYaml
180 && data
181 .extra_data
182 .as_ref()
183 .and_then(|extra_data| extra_data.get("workspace_members"))
184 .and_then(|members| members.as_array())
185 .is_some_and(|members| !members.is_empty())
186 },
187 },
188];
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIter)]
193pub(super) enum PostAssemblyPassId {
194 SwiftMerge,
195 CondaRootfsMerge,
196 NpmResourceAssign,
197 PythonRequirementsAssign,
198 IvyDependenciesPropertiesAssign,
199 ClojureDepsEdnAssign,
200 FileReferenceResolve,
201 RpmYumdbMerge,
202 NpmWorkspaceMerge,
203 CargoWorkspaceMerge,
204 MixUmbrellaMerge,
205 MavenReactorAssign,
206 GradleMultiProjectAssign,
207 UvWorkspaceAssign,
208 DartWorkspaceMerge,
209 NugetCpmResolve,
210 CargoResourceAssign,
211 ComposerResourceAssign,
212 RubyResourceAssign,
213 NixFlakeCompatMerge,
214 BazelPrune,
215}
216
217struct PostAssemblyPass {
222 id: PostAssemblyPassId,
223 should_run: fn(&PostAssemblyInputs) -> bool,
224 run: fn(
225 &mut [FileInfo],
226 &mut Vec<Package>,
227 &mut Vec<TopLevelDependency>,
228 &topology::TopologyPlan,
229 ),
230}
231
232static POST_ASSEMBLY_PASSES: &[PostAssemblyPass] = &[
233 PostAssemblyPass {
234 id: PostAssemblyPassId::SwiftMerge,
235 should_run: |inputs| inputs.has_any_file_datasource(SWIFT_POST_ASSEMBLY_DATASOURCE_IDS),
236 run: |files, packages, dependencies, _plan| {
237 swift_merge::assemble_swift_packages(files, packages, dependencies)
238 },
239 },
240 PostAssemblyPass {
241 id: PostAssemblyPassId::CondaRootfsMerge,
242 should_run: |inputs| {
243 inputs.has_all_file_datasources(CONDA_ROOTFS_POST_ASSEMBLY_DATASOURCE_IDS)
244 },
245 run: |files, packages, dependencies, _plan| {
246 conda_rootfs_merge::merge_conda_rootfs_metadata(files, packages, dependencies)
247 },
248 },
249 PostAssemblyPass {
250 id: PostAssemblyPassId::NpmResourceAssign,
251 should_run: |inputs| inputs.has_package_type(PackageType::Npm),
252 run: |files, packages, _dependencies, _plan| {
253 npm_resource_assign::assign_npm_package_resources(files, packages)
254 },
255 },
256 PostAssemblyPass {
257 id: PostAssemblyPassId::PythonRequirementsAssign,
258 should_run: |inputs| {
259 inputs.has_package_type(PackageType::Pypi)
260 && inputs.has_any_file_datasource(&[DatasourceId::PipRequirements])
261 },
262 run: |files, packages, dependencies, _plan| {
263 python_requirements_assign::assign_python_requirements_to_projects(
264 files,
265 packages,
266 dependencies,
267 )
268 },
269 },
270 PostAssemblyPass {
271 id: PostAssemblyPassId::IvyDependenciesPropertiesAssign,
272 should_run: |inputs| {
273 inputs.has_package_type(PackageType::Ivy)
274 && inputs.has_any_file_datasource(&[DatasourceId::AntIvyDependenciesProperties])
275 },
276 run: |files, packages, dependencies, _plan| {
277 ivy_dependencies_properties_assign::assign_ivy_dependencies_properties_to_projects(
278 files,
279 packages,
280 dependencies,
281 )
282 },
283 },
284 PostAssemblyPass {
285 id: PostAssemblyPassId::ClojureDepsEdnAssign,
286 should_run: |inputs| {
287 inputs.has_package_type(PackageType::Maven)
288 && inputs.has_all_file_datasources(&[
289 DatasourceId::ClojureProjectClj,
290 DatasourceId::ClojureDepsEdn,
291 ])
292 },
293 run: |files, packages, dependencies, _plan| {
294 clojure_deps_assign::assign_clojure_deps_edn_to_projects(files, packages, dependencies)
295 },
296 },
297 PostAssemblyPass {
298 id: PostAssemblyPassId::FileReferenceResolve,
299 should_run: |inputs| {
300 file_ref_resolve::has_relevant_file_reference_datasource_ids(
301 &inputs.file_datasource_ids,
302 )
303 },
304 run: |files, packages, dependencies, _plan| {
305 file_ref_resolve::resolve_file_references(files, packages, dependencies)
306 },
307 },
308 PostAssemblyPass {
309 id: PostAssemblyPassId::RpmYumdbMerge,
310 should_run: |inputs| {
311 inputs.has_any_file_datasource(&[DatasourceId::RpmYumdb])
312 && inputs.has_any_file_datasource(RPM_INSTALLED_DATABASE_DATASOURCE_IDS)
313 },
314 run: |files, packages, _dependencies, _plan| {
315 file_ref_resolve::merge_rpm_yumdb_metadata(files, packages)
316 },
317 },
318 PostAssemblyPass {
319 id: PostAssemblyPassId::NpmWorkspaceMerge,
320 should_run: |inputs| inputs.has_marker(WorkspaceMarker::NpmWorkspace),
321 run: |files, packages, dependencies, plan| {
322 plan.apply_npm_workspace_domains(files, packages, dependencies)
323 },
324 },
325 PostAssemblyPass {
326 id: PostAssemblyPassId::CargoWorkspaceMerge,
327 should_run: |inputs| inputs.has_marker(WorkspaceMarker::CargoWorkspace),
328 run: |files, packages, dependencies, plan| {
329 plan.apply_cargo_workspace_domains(files, packages, dependencies)
330 },
331 },
332 PostAssemblyPass {
333 id: PostAssemblyPassId::MixUmbrellaMerge,
334 should_run: |inputs| inputs.has_marker(WorkspaceMarker::MixUmbrella),
335 run: |files, packages, dependencies, plan| {
336 plan.apply_mix_umbrella_domains(files, packages, dependencies)
337 },
338 },
339 PostAssemblyPass {
340 id: PostAssemblyPassId::MavenReactorAssign,
341 should_run: |inputs| inputs.has_marker(WorkspaceMarker::MavenReactor),
342 run: |files, _packages, _dependencies, plan| plan.apply_maven_reactor_domains(files),
343 },
344 PostAssemblyPass {
345 id: PostAssemblyPassId::GradleMultiProjectAssign,
346 should_run: |inputs| inputs.has_marker(WorkspaceMarker::GradleMultiProject),
347 run: |files, packages, dependencies, plan| {
348 plan.apply_gradle_multi_project_domains(files, packages, dependencies)
349 },
350 },
351 PostAssemblyPass {
352 id: PostAssemblyPassId::UvWorkspaceAssign,
353 should_run: |inputs| inputs.has_marker(WorkspaceMarker::UvWorkspace),
354 run: |files, _packages, dependencies, plan| {
355 plan.apply_uv_workspace_domains(files, dependencies)
356 },
357 },
358 PostAssemblyPass {
359 id: PostAssemblyPassId::DartWorkspaceMerge,
360 should_run: |inputs| inputs.has_marker(WorkspaceMarker::DartWorkspace),
361 run: |files, packages, dependencies, plan| {
362 plan.apply_dart_workspace_domains(files, packages, dependencies)
363 },
364 },
365 PostAssemblyPass {
366 id: PostAssemblyPassId::NugetCpmResolve,
367 should_run: |inputs| {
368 inputs.has_any_file_datasource(NUGET_CPM_CONFIG_DATASOURCE_IDS)
369 && inputs.has_any_file_datasource(NUGET_CPM_PROJECT_DATASOURCE_IDS)
370 },
371 run: |files, _packages, dependencies, _plan| {
372 nuget_cpm_resolve::resolve_nuget_cpm_versions(files, dependencies)
373 },
374 },
375 PostAssemblyPass {
376 id: PostAssemblyPassId::CargoResourceAssign,
377 should_run: |inputs| inputs.has_package_type(PackageType::Cargo),
378 run: |files, packages, _dependencies, _plan| {
379 resource_assign::assign_resources_for(PackageType::Cargo, files, packages)
380 },
381 },
382 PostAssemblyPass {
383 id: PostAssemblyPassId::ComposerResourceAssign,
384 should_run: |inputs| inputs.has_package_type(PackageType::Composer),
385 run: |files, packages, _dependencies, _plan| {
386 resource_assign::assign_resources_for(PackageType::Composer, files, packages)
387 },
388 },
389 PostAssemblyPass {
390 id: PostAssemblyPassId::RubyResourceAssign,
391 should_run: |inputs| inputs.has_package_type(PackageType::Gem),
392 run: |files, packages, _dependencies, _plan| {
393 resource_assign::assign_resources_for(PackageType::Gem, files, packages)
394 },
395 },
396 PostAssemblyPass {
397 id: PostAssemblyPassId::NixFlakeCompatMerge,
398 should_run: |inputs| {
399 inputs.has_any_file_datasource(&[DatasourceId::NixDefaultNix])
400 && inputs.has_any_file_datasource(&[
401 DatasourceId::NixFlakeNix,
402 DatasourceId::NixFlakeLock,
403 ])
404 },
405 run: |files, packages, _dependencies, _plan| {
406 nix_flake_compat_merge::attach_flake_compat_default_files(files, packages)
407 },
408 },
409 PostAssemblyPass {
410 id: PostAssemblyPassId::BazelPrune,
411 should_run: |inputs| inputs.has_package_type(PackageType::Bazel),
412 run: |files, packages, dependencies, _plan| {
413 bazel_prune::prune_unused_bazel_packages(files, packages, dependencies)
414 },
415 },
416];
417
418const SWIFT_POST_ASSEMBLY_DATASOURCE_IDS: &[DatasourceId] = &[
419 DatasourceId::SwiftPackageManifestJson,
420 DatasourceId::SwiftPackageResolved,
421 DatasourceId::SwiftPackageShowDependencies,
422];
423
424const CONDA_ROOTFS_POST_ASSEMBLY_DATASOURCE_IDS: &[DatasourceId] =
425 &[DatasourceId::CondaMetaJson, DatasourceId::CondaMetaYaml];
426
427const RPM_INSTALLED_DATABASE_DATASOURCE_IDS: &[DatasourceId] = &[
428 DatasourceId::RpmInstalledDatabaseBdb,
429 DatasourceId::RpmInstalledDatabaseNdb,
430 DatasourceId::RpmInstalledDatabaseSqlite,
431];
432
433const NUGET_CPM_CONFIG_DATASOURCE_IDS: &[DatasourceId] = &[
434 DatasourceId::NugetDirectoryBuildProps,
435 DatasourceId::NugetDirectoryPackagesProps,
436];
437
438const NUGET_CPM_PROJECT_DATASOURCE_IDS: &[DatasourceId] = &[
439 DatasourceId::NugetCsproj,
440 DatasourceId::NugetFsproj,
441 DatasourceId::NugetVbproj,
442];
443
444#[derive(Default)]
445struct PostAssemblyInputs {
446 package_types: HashSet<PackageType>,
447 file_datasource_ids: HashSet<DatasourceId>,
448 markers: HashSet<WorkspaceMarker>,
449}
450
451pub(super) fn run_post_assembly_passes(
452 files: &mut [FileInfo],
453 packages: &mut Vec<Package>,
454 dependencies: &mut Vec<TopLevelDependency>,
455 topology_plan: &topology::TopologyPlan,
456) {
457 let inputs = PostAssemblyInputs::collect(files, packages);
458
459 for pass in POST_ASSEMBLY_PASSES {
460 if !(pass.should_run)(&inputs) {
461 continue;
462 }
463
464 log::trace!("running post-assembly pass {:?}", pass.id);
465 (pass.run)(files, packages, dependencies, topology_plan);
466 }
467}
468
469impl PostAssemblyInputs {
470 fn collect(files: &[FileInfo], packages: &[Package]) -> Self {
471 let mut inputs = Self {
472 package_types: packages
473 .iter()
474 .filter_map(|package| package.package_type)
475 .collect(),
476 ..Self::default()
477 };
478
479 for file in files {
480 for package_data in &file.package_data {
481 let Some(datasource_id) = package_data.datasource_id else {
482 continue;
483 };
484
485 inputs.file_datasource_ids.insert(datasource_id);
486
487 for detector in MARKER_DETECTORS {
488 if !inputs.markers.contains(&detector.marker)
489 && (detector.detect)(datasource_id, package_data)
490 {
491 inputs.markers.insert(detector.marker);
492 }
493 }
494 }
495 }
496
497 inputs
498 }
499
500 fn has_package_type(&self, package_type: PackageType) -> bool {
501 self.package_types.contains(&package_type)
502 }
503
504 fn has_marker(&self, marker: WorkspaceMarker) -> bool {
505 self.markers.contains(&marker)
506 }
507
508 fn has_any_file_datasource(&self, datasource_ids: &[DatasourceId]) -> bool {
509 datasource_ids
510 .iter()
511 .any(|datasource_id| self.file_datasource_ids.contains(datasource_id))
512 }
513
514 fn has_all_file_datasources(&self, datasource_ids: &[DatasourceId]) -> bool {
515 datasource_ids
516 .iter()
517 .all(|datasource_id| self.file_datasource_ids.contains(datasource_id))
518 }
519}
520
521pub static ASSEMBLERS: &[AssemblerConfig] = &[
522 AssemblerConfig {
528 datasource_ids: &[
529 DatasourceId::BunLock,
530 DatasourceId::BunLockb,
531 DatasourceId::NpmPackageJson,
532 DatasourceId::NpmPackageLockJson,
533 DatasourceId::YarnLock,
534 DatasourceId::YarnLockV1,
535 DatasourceId::YarnLockV2,
536 DatasourceId::YarnPnpCjs,
537 DatasourceId::PnpmLockYaml,
538 DatasourceId::PnpmWorkspaceYaml,
539 ],
540 sibling_file_patterns: &[
541 "package.json",
542 "bun.lock",
543 "bun.lockb",
544 ".package-lock.json",
545 "package-lock.json",
546 ".npm-shrinkwrap.json",
547 "npm-shrinkwrap.json",
548 "yarn.lock",
549 ".pnp.cjs",
550 "pnpm-lock.yaml",
551 "shrinkwrap.yaml",
552 "pnpm-workspace.yaml",
553 ],
554 mode: AssemblyMode::SiblingMerge,
555 directory_merger: None,
556 },
557 AssemblerConfig {
559 datasource_ids: &[DatasourceId::CargoToml, DatasourceId::CargoLock],
560 sibling_file_patterns: &["Cargo.toml", "Cargo.lock"],
561 mode: AssemblyMode::SiblingMerge,
562 directory_merger: None,
563 },
564 AssemblerConfig {
566 datasource_ids: &[
567 DatasourceId::JuliaProjectToml,
568 DatasourceId::JuliaManifestToml,
569 ],
570 sibling_file_patterns: &["Project.toml", "Manifest.toml"],
571 mode: AssemblyMode::SiblingMerge,
572 directory_merger: None,
573 },
574 AssemblerConfig {
576 datasource_ids: &[DatasourceId::RebarConfig, DatasourceId::RebarLock],
577 sibling_file_patterns: &["rebar.config", "rebar.lock"],
578 mode: AssemblyMode::SiblingMerge,
579 directory_merger: None,
580 },
581 AssemblerConfig {
587 datasource_ids: &[DatasourceId::HexMixExs, DatasourceId::HexMixLock],
588 sibling_file_patterns: &["mix.exs", "mix.lock"],
589 mode: AssemblyMode::SiblingMerge,
590 directory_merger: None,
591 },
592 AssemblerConfig {
597 datasource_ids: &[DatasourceId::ErlangOtpAppSrc],
598 sibling_file_patterns: &["*.app.src"],
599 mode: AssemblyMode::OnePerPackageData,
600 directory_merger: None,
601 },
602 AssemblerConfig {
604 datasource_ids: &[
605 DatasourceId::CarthageCartfile,
606 DatasourceId::CarthageCartfileResolved,
607 ],
608 sibling_file_patterns: &["Cartfile", "Cartfile.private", "Cartfile.resolved"],
609 mode: AssemblyMode::SiblingMerge,
610 directory_merger: None,
611 },
612 AssemblerConfig {
614 datasource_ids: &[
615 DatasourceId::CocoapodsPodspec,
616 DatasourceId::CocoapodsPodspecJson,
617 DatasourceId::CocoapodsPodfile,
618 DatasourceId::CocoapodsPodfileLock,
619 ],
620 sibling_file_patterns: &["*.podspec", "*.podspec.json", "Podfile", "Podfile.lock"],
621 mode: AssemblyMode::SiblingMerge,
622 directory_merger: Some(merge_cocoapods),
623 },
624 AssemblerConfig {
626 datasource_ids: &[DatasourceId::PhpComposerJson, DatasourceId::PhpComposerLock],
627 sibling_file_patterns: &[
628 "*composer.json",
629 "composer.*.json",
630 "*composer.lock",
631 "composer.*.lock",
632 ],
633 mode: AssemblyMode::SiblingMerge,
634 directory_merger: None,
635 },
636 AssemblerConfig {
638 datasource_ids: &[
639 DatasourceId::GoMod,
640 DatasourceId::GoModGraph,
641 DatasourceId::GoSum,
642 DatasourceId::GoWork,
643 DatasourceId::Godeps,
644 ],
645 sibling_file_patterns: &[
646 "go.mod",
647 "go.work",
648 "go.mod.graph",
649 "go.modgraph",
650 "go.sum",
651 "Godeps.json",
652 ],
653 mode: AssemblyMode::SiblingMerge,
654 directory_merger: None,
655 },
656 AssemblerConfig {
658 datasource_ids: &[DatasourceId::PubspecYaml, DatasourceId::PubspecLock],
659 sibling_file_patterns: &["pubspec.yaml", "pubspec.lock"],
660 mode: AssemblyMode::SiblingMerge,
661 directory_merger: None,
662 },
663 AssemblerConfig {
665 datasource_ids: &[DatasourceId::PixiToml, DatasourceId::PixiLock],
666 sibling_file_patterns: &["pixi.toml", "pixi.lock"],
667 mode: AssemblyMode::SiblingMerge,
668 directory_merger: None,
669 },
670 AssemblerConfig {
671 datasource_ids: &[DatasourceId::NixFlakeNix, DatasourceId::NixFlakeLock],
672 sibling_file_patterns: &["flake.nix", "flake.lock"],
673 mode: AssemblyMode::SiblingMerge,
674 directory_merger: None,
675 },
676 AssemblerConfig {
677 datasource_ids: &[DatasourceId::NixDefaultNix],
678 sibling_file_patterns: &["default.nix"],
679 mode: AssemblyMode::OnePerPackageData,
680 directory_merger: None,
681 },
682 AssemblerConfig {
684 datasource_ids: &[DatasourceId::HelmChartYaml, DatasourceId::HelmChartLock],
685 sibling_file_patterns: &["Chart.yaml", "Chart.lock"],
686 mode: AssemblyMode::SiblingMerge,
687 directory_merger: None,
688 },
689 AssemblerConfig {
690 datasource_ids: &[
691 DatasourceId::HackageCabal,
692 DatasourceId::HackageCabalProject,
693 DatasourceId::HackageStackYaml,
694 ],
695 sibling_file_patterns: &["*.cabal", "cabal.project", "stack.yaml"],
696 mode: AssemblyMode::SiblingMerge,
697 directory_merger: Some(merge_hackage),
698 },
699 AssemblerConfig {
701 datasource_ids: &[
702 DatasourceId::ChefCookbookMetadataJson,
703 DatasourceId::ChefCookbookMetadataRb,
704 ],
705 sibling_file_patterns: &["metadata.json", "metadata.rb"],
706 mode: AssemblyMode::SiblingMerge,
707 directory_merger: None,
708 },
709 AssemblerConfig {
711 datasource_ids: &[
712 DatasourceId::ConanConanFilePy,
713 DatasourceId::ConanConanFileTxt,
714 DatasourceId::ConanLock,
715 DatasourceId::ConanConanDataYml,
716 ],
717 sibling_file_patterns: &[
718 "conanfile.py",
719 "conanfile.txt",
720 "conan.lock",
721 "conandata.yml",
722 ],
723 mode: AssemblyMode::SiblingMerge,
724 directory_merger: None,
725 },
726 AssemblerConfig {
733 datasource_ids: &[DatasourceId::VcpkgControl, DatasourceId::VcpkgJson],
734 sibling_file_patterns: &["CONTROL", "vcpkg.json"],
735 mode: AssemblyMode::OnePerPackageData,
736 directory_merger: None,
737 },
738 AssemblerConfig {
740 datasource_ids: &[
741 DatasourceId::MavenPom,
742 DatasourceId::MavenPomProperties,
743 DatasourceId::JavaJarManifest,
744 DatasourceId::JavaOsgiManifest,
745 ],
746 sibling_file_patterns: &[
747 "pom.xml",
748 "*.pom",
749 "pom.properties",
750 "**/META-INF/MANIFEST.MF",
751 ],
752 mode: AssemblyMode::SiblingMergePerIdentity,
755 directory_merger: None,
756 },
757 AssemblerConfig {
762 datasource_ids: &[DatasourceId::ClojureProjectClj],
763 sibling_file_patterns: &["project.clj"],
764 mode: AssemblyMode::OnePerPackageData,
765 directory_merger: None,
766 },
767 AssemblerConfig {
771 datasource_ids: &[DatasourceId::SbtBuildSbt],
772 sibling_file_patterns: &["build.sbt"],
773 mode: AssemblyMode::OnePerPackageData,
774 directory_merger: None,
775 },
776 AssemblerConfig {
777 datasource_ids: &[DatasourceId::PypiWheel, DatasourceId::PypiPipOriginJson],
778 sibling_file_patterns: &["*.whl", "origin.json"],
779 mode: AssemblyMode::SiblingMergePerIdentity,
784 directory_merger: None,
785 },
786 AssemblerConfig {
788 datasource_ids: &[
789 DatasourceId::PypiPyprojectToml,
790 DatasourceId::PypiPoetryPyprojectToml,
791 DatasourceId::PypiSetupPy,
792 DatasourceId::PypiSetupCfg,
793 DatasourceId::PypiWheelMetadata,
794 DatasourceId::PypiEgg,
795 DatasourceId::PypiEggPkginfo,
796 DatasourceId::PypiEditableEggPkginfo,
797 DatasourceId::PypiJson,
798 DatasourceId::PypiSdist,
799 DatasourceId::PypiSdistPkginfo,
800 DatasourceId::PypiInspectDeplock,
801 DatasourceId::PipRequirements,
802 DatasourceId::PypiPoetryLock,
803 DatasourceId::PypiPylockToml,
804 DatasourceId::PypiUvLock,
805 DatasourceId::Pipfile,
806 DatasourceId::PipfileLock,
807 ],
808 sibling_file_patterns: &[
809 "pyproject.toml",
810 "setup.py",
811 "setup.cfg",
812 "PKG-INFO",
813 "METADATA",
814 "pypi.json",
815 "pip-inspect.deplock",
816 "*.tar.gz",
817 "*.tgz",
818 "*.tar.bz2",
819 "*.tar.xz",
820 "*.zip",
821 "requirements*.txt",
822 "Pipfile",
823 "Pipfile.lock",
824 "poetry.lock",
825 "pylock.toml",
826 "pylock.*.toml",
827 "uv.lock",
828 ],
829 mode: AssemblyMode::SiblingMerge,
830 directory_merger: None,
831 },
832 AssemblerConfig {
833 datasource_ids: &[DatasourceId::DenoJson, DatasourceId::DenoLock],
834 sibling_file_patterns: &["deno.json", "deno.jsonc", "deno.lock"],
835 mode: AssemblyMode::SiblingMerge,
836 directory_merger: None,
837 },
838 AssemblerConfig {
840 datasource_ids: &[
841 DatasourceId::GemArchiveExtracted,
842 DatasourceId::Gemspec,
843 DatasourceId::GemspecExtracted,
844 DatasourceId::Gemfile,
845 DatasourceId::GemfileExtracted,
846 DatasourceId::GemfileLock,
847 DatasourceId::GemfileLockExtracted,
848 ],
849 sibling_file_patterns: &[
850 "metadata.gz-extract",
851 "**/data.gz-extract/*.gemspec",
852 "**/data.gz-extract/Gemfile",
853 "**/data.gz-extract/Gemfile.lock",
854 "*.gemspec",
855 "Gemfile",
856 "Gemfile.lock",
857 ],
858 mode: AssemblyMode::SiblingMergePerIdentity,
864 directory_merger: None,
865 },
866 AssemblerConfig {
871 datasource_ids: &[DatasourceId::GemGemspecInstalledSpecifications],
872 sibling_file_patterns: &["**/specifications/*.gemspec"],
873 mode: AssemblyMode::OnePerPackageData,
874 directory_merger: None,
875 },
876 AssemblerConfig {
877 datasource_ids: &[DatasourceId::GemArchive],
878 sibling_file_patterns: &["*.gem"],
879 mode: AssemblyMode::OnePerPackageData,
880 directory_merger: None,
881 },
882 AssemblerConfig {
885 datasource_ids: &[DatasourceId::CondaMetaYaml, DatasourceId::CondaYaml],
886 sibling_file_patterns: &[
887 "meta.yaml",
888 "meta.yml",
889 "recipe.yaml",
890 "recipe.yml",
891 "environment.yml",
892 "environment.yaml",
893 "conda.yaml",
894 "conda.yml",
895 "*conda*.yaml",
896 "*conda*.yml",
897 "env.yaml",
898 "env.yml",
899 "*env*.yaml",
900 "*env*.yml",
901 "*environment*.yaml",
902 "*environment*.yml",
903 "*.json",
904 ],
905 mode: AssemblyMode::SiblingMerge,
906 directory_merger: None,
907 },
908 AssemblerConfig {
913 datasource_ids: &[DatasourceId::CondaMetaJson],
914 sibling_file_patterns: &["*.json"],
915 mode: AssemblyMode::OnePerPackageData,
916 directory_merger: None,
917 },
918 AssemblerConfig {
920 datasource_ids: &[DatasourceId::RpmSpecfile],
921 sibling_file_patterns: &["*.spec"],
922 mode: AssemblyMode::OnePerPackageData,
923 directory_merger: None,
924 },
925 AssemblerConfig {
927 datasource_ids: &[
928 DatasourceId::DebianControlInSource,
929 DatasourceId::DebianCopyrightInSource,
930 ],
931 sibling_file_patterns: &["control", "copyright"],
932 mode: AssemblyMode::SiblingMerge,
933 directory_merger: Some(merge_debian_source),
934 },
935 AssemblerConfig {
937 datasource_ids: &[DatasourceId::BuildGradle, DatasourceId::GradleLockfile],
938 sibling_file_patterns: &["build.gradle", "build.gradle.kts", "gradle.lockfile"],
939 mode: AssemblyMode::SiblingMerge,
940 directory_merger: None,
941 },
942 AssemblerConfig {
943 datasource_ids: &[DatasourceId::GradleModule],
944 sibling_file_patterns: &["*.module"],
945 mode: AssemblyMode::OnePerPackageData,
946 directory_merger: None,
947 },
948 AssemblerConfig {
955 datasource_ids: &[
956 DatasourceId::HuggingfaceModelCard,
957 DatasourceId::HuggingfaceConfigJson,
958 DatasourceId::HuggingfaceModelIndexJson,
959 ],
960 sibling_file_patterns: &["README.md", "config.json", "model_index.json"],
961 mode: AssemblyMode::SiblingMerge,
962 directory_merger: Some(merge_huggingface),
963 },
964 AssemblerConfig {
966 datasource_ids: &[
967 DatasourceId::CpanMetaJson,
968 DatasourceId::CpanMetaYml,
969 DatasourceId::CpanManifest,
970 DatasourceId::CpanDistIni,
971 DatasourceId::CpanMakefile,
972 ],
973 sibling_file_patterns: &[
974 "META.json",
975 "META.yml",
976 "MANIFEST",
977 "dist.ini",
978 "Makefile.PL",
979 ],
980 mode: AssemblyMode::SiblingMerge,
981 directory_merger: None,
982 },
983 AssemblerConfig {
985 datasource_ids: &[
986 DatasourceId::NugetCsproj,
987 DatasourceId::NugetFsproj,
988 DatasourceId::NugetNuspec,
989 DatasourceId::NugetNupkg,
990 DatasourceId::NugetProjectJson,
991 DatasourceId::NugetProjectLockJson,
992 DatasourceId::NugetPackagesConfig,
993 DatasourceId::NugetPackagesLock,
994 DatasourceId::NugetVbproj,
995 ],
996 sibling_file_patterns: &[
997 "*.csproj",
998 "*.fsproj",
999 "*.nuspec",
1000 "*.nupkg",
1001 "project.json",
1002 "project.lock.json",
1003 "packages.config",
1004 "packages.lock.json",
1005 "*.packages.lock.json",
1006 "*.vbproj",
1007 ],
1008 mode: AssemblyMode::SiblingMergePerIdentity,
1014 directory_merger: None,
1015 },
1016 AssemblerConfig {
1017 datasource_ids: &[DatasourceId::NugetDepsJson],
1018 sibling_file_patterns: &["*.deps.json"],
1019 mode: AssemblyMode::OnePerPackageData,
1020 directory_merger: None,
1021 },
1022 AssemblerConfig {
1024 datasource_ids: &[
1025 DatasourceId::SwiftPackageManifestJson,
1026 DatasourceId::SwiftPackageResolved,
1027 DatasourceId::SwiftPackageShowDependencies,
1028 ],
1029 sibling_file_patterns: &[
1030 "Package.swift.json",
1031 "Package.swift.deplock",
1032 "Package.resolved",
1033 ".package.resolved",
1034 "swift-show-dependencies.deplock",
1035 ],
1036 mode: AssemblyMode::SiblingMerge,
1037 directory_merger: Some(merge_swift_skip),
1038 },
1039 AssemblerConfig {
1043 datasource_ids: &[DatasourceId::VscodeExtensionVsixManifest],
1044 sibling_file_patterns: &["extension.vsixmanifest"],
1045 mode: AssemblyMode::OnePerPackageData,
1046 directory_merger: None,
1047 },
1048 AssemblerConfig {
1055 datasource_ids: &[DatasourceId::BowerJson],
1056 sibling_file_patterns: &["bower.json"],
1057 mode: AssemblyMode::SiblingMerge,
1058 directory_merger: None,
1059 },
1060 AssemblerConfig {
1062 datasource_ids: &[DatasourceId::CranDescription],
1063 sibling_file_patterns: &["DESCRIPTION"],
1064 mode: AssemblyMode::SiblingMerge,
1065 directory_merger: None,
1066 },
1067 AssemblerConfig {
1069 datasource_ids: &[DatasourceId::FreebsdCompactManifest],
1070 sibling_file_patterns: &["+COMPACT_MANIFEST"],
1071 mode: AssemblyMode::SiblingMerge,
1072 directory_merger: None,
1073 },
1074 AssemblerConfig {
1076 datasource_ids: &[DatasourceId::HaxelibJson],
1077 sibling_file_patterns: &["haxelib.json"],
1078 mode: AssemblyMode::SiblingMerge,
1079 directory_merger: None,
1080 },
1081 AssemblerConfig {
1082 datasource_ids: &[DatasourceId::Gitmodules],
1083 sibling_file_patterns: &[".gitmodules"],
1084 mode: AssemblyMode::SiblingMerge,
1085 directory_merger: None,
1086 },
1087 AssemblerConfig {
1092 datasource_ids: &[DatasourceId::OpamFile],
1093 sibling_file_patterns: &["opam", "*.opam"],
1094 mode: AssemblyMode::SiblingMergePerIdentity,
1095 directory_merger: None,
1096 },
1097 AssemblerConfig {
1098 datasource_ids: &[DatasourceId::RpmYumdb],
1099 sibling_file_patterns: &["**/var/lib/yum/yumdb/*/*/from_repo"],
1100 mode: AssemblyMode::OnePerPackageData,
1101 directory_merger: None,
1102 },
1103 AssemblerConfig {
1105 datasource_ids: &[DatasourceId::MicrosoftUpdateManifestMum],
1106 sibling_file_patterns: &["*.mum"],
1107 mode: AssemblyMode::SiblingMerge,
1108 directory_merger: Some(merge_windows_update),
1109 },
1110 AssemblerConfig {
1112 datasource_ids: &[DatasourceId::AutotoolsConfigure],
1113 sibling_file_patterns: &["configure", "configure.ac"],
1114 mode: AssemblyMode::SiblingMerge,
1115 directory_merger: None,
1116 },
1117 AssemblerConfig {
1123 datasource_ids: &[DatasourceId::BazelBuild],
1124 sibling_file_patterns: &["BUILD"],
1125 mode: AssemblyMode::SiblingMerge,
1126 directory_merger: None,
1127 },
1128 AssemblerConfig {
1129 datasource_ids: &[DatasourceId::BazelModule],
1130 sibling_file_patterns: &["MODULE.bazel"],
1131 mode: AssemblyMode::OnePerPackageData,
1132 directory_merger: None,
1133 },
1134 AssemblerConfig {
1136 datasource_ids: &[DatasourceId::BuckFile, DatasourceId::BuckMetadata],
1137 sibling_file_patterns: &["BUCK", "METADATA.bzl", ".buckconfig"],
1138 mode: AssemblyMode::SiblingMerge,
1139 directory_merger: None,
1140 },
1141 AssemblerConfig {
1149 datasource_ids: &[DatasourceId::MesonBuild],
1150 sibling_file_patterns: &["meson.build"],
1151 mode: AssemblyMode::OnePerPackageData,
1152 directory_merger: None,
1153 },
1154 AssemblerConfig {
1156 datasource_ids: &[DatasourceId::AntIvyXml],
1157 sibling_file_patterns: &["ivy.xml"],
1158 mode: AssemblyMode::SiblingMerge,
1159 directory_merger: None,
1160 },
1161 AssemblerConfig {
1163 datasource_ids: &[
1164 DatasourceId::JavaJar,
1165 DatasourceId::JavaWarArchive,
1166 DatasourceId::AndroidAarLibrary,
1167 ],
1168 sibling_file_patterns: &["*.jar", "*.war", "*.aar"],
1169 mode: AssemblyMode::OnePerPackageData,
1170 directory_merger: None,
1171 },
1172 AssemblerConfig {
1174 datasource_ids: &[DatasourceId::MeteorPackage],
1175 sibling_file_patterns: &["package.js"],
1176 mode: AssemblyMode::SiblingMerge,
1177 directory_merger: None,
1178 },
1179 AssemblerConfig {
1183 datasource_ids: &[DatasourceId::AlpineInstalledDb],
1184 sibling_file_patterns: &["installed"],
1185 mode: AssemblyMode::OnePerPackageData,
1186 directory_merger: None,
1187 },
1188 AssemblerConfig {
1189 datasource_ids: &[DatasourceId::AlpineApkbuild],
1190 sibling_file_patterns: &["APKBUILD"],
1191 mode: AssemblyMode::SiblingMerge,
1192 directory_merger: None,
1193 },
1194 AssemblerConfig {
1198 datasource_ids: &[
1199 DatasourceId::ArchSrcinfo,
1200 DatasourceId::ArchPkginfo,
1201 DatasourceId::ArchAurinfo,
1202 ],
1203 sibling_file_patterns: &[".SRCINFO", ".PKGINFO", ".AURINFO"],
1204 mode: AssemblyMode::OnePerPackageData,
1205 directory_merger: None,
1206 },
1207 AssemblerConfig {
1209 datasource_ids: &[
1210 DatasourceId::RpmInstalledDatabaseBdb,
1211 DatasourceId::RpmInstalledDatabaseNdb,
1212 DatasourceId::RpmInstalledDatabaseSqlite,
1213 DatasourceId::RpmMarinerManifest,
1214 ],
1215 sibling_file_patterns: &[
1216 "Packages",
1217 "Packages.db",
1218 "rpmdb.sqlite",
1219 "container-manifest-2",
1220 ],
1221 mode: AssemblyMode::OnePerPackageData,
1222 directory_merger: None,
1223 },
1224 AssemblerConfig {
1225 datasource_ids: &[DatasourceId::RpmArchive],
1226 sibling_file_patterns: &["*.rpm", "*.srpm"],
1227 mode: AssemblyMode::OnePerPackageData,
1228 directory_merger: None,
1229 },
1230 AssemblerConfig {
1232 datasource_ids: &[DatasourceId::DebianDeb],
1233 sibling_file_patterns: &["*.deb"],
1234 mode: AssemblyMode::OnePerPackageData,
1235 directory_merger: None,
1236 },
1237 AssemblerConfig {
1238 datasource_ids: &[
1239 DatasourceId::DebianInstalledStatusDb,
1240 DatasourceId::DebianDistrolessInstalledDb,
1241 ],
1242 sibling_file_patterns: &["status"],
1243 mode: AssemblyMode::OnePerPackageData,
1244 directory_merger: None,
1245 },
1246 AssemblerConfig {
1247 datasource_ids: &[
1248 DatasourceId::DebianControlExtractedDeb,
1249 DatasourceId::DebianMd5SumsInExtractedDeb,
1250 ],
1251 sibling_file_patterns: &["control", "md5sums"],
1252 mode: AssemblyMode::SiblingMerge,
1253 directory_merger: None,
1254 },
1255 AssemblerConfig {
1256 datasource_ids: &[DatasourceId::DebianSourceControlDsc],
1257 sibling_file_patterns: &["*.dsc"],
1258 mode: AssemblyMode::OnePerPackageData,
1259 directory_merger: None,
1260 },
1261 AssemblerConfig {
1262 datasource_ids: &[DatasourceId::AboutFile],
1263 sibling_file_patterns: &["*.ABOUT"],
1264 mode: AssemblyMode::OnePerPackageData,
1265 directory_merger: None,
1266 },
1267 AssemblerConfig {
1268 datasource_ids: &[
1269 DatasourceId::BitbakeRecipe,
1270 DatasourceId::BitbakeRecipeAppend,
1271 ],
1272 sibling_file_patterns: &["*.bb", "*.bbappend"],
1273 mode: AssemblyMode::SiblingMergePerIdentity,
1280 directory_merger: None,
1281 },
1282];
1283
1284#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1297pub(super) enum UnassembledReason {
1298 NotAPackage,
1301 BinaryArtifact,
1304 SupplementaryMetadata,
1307 DependenciesOnlyNoIdentity,
1310}
1311
1312pub(super) static UNASSEMBLED_DATASOURCE_IDS: &[(DatasourceId, UnassembledReason)] = &[
1313 (DatasourceId::Readme, UnassembledReason::NotAPackage),
1314 (DatasourceId::EtcOsRelease, UnassembledReason::NotAPackage),
1315 (
1316 DatasourceId::AndroidManifestXml,
1317 UnassembledReason::NotAPackage,
1318 ),
1319 (
1320 DatasourceId::AndroidSoongMetadata,
1321 UnassembledReason::NotAPackage,
1322 ),
1323 (DatasourceId::Dockerfile, UnassembledReason::NotAPackage),
1324 (DatasourceId::GradleSettings, UnassembledReason::NotAPackage),
1329 (DatasourceId::OciImageIndex, UnassembledReason::NotAPackage),
1330 (
1331 DatasourceId::OciImageManifest,
1332 UnassembledReason::NotAPackage,
1333 ),
1334 (DatasourceId::Axis2ModuleXml, UnassembledReason::NotAPackage),
1335 (
1336 DatasourceId::JavaEarApplicationXml,
1337 UnassembledReason::NotAPackage,
1338 ),
1339 (DatasourceId::JavaWarWebXml, UnassembledReason::NotAPackage),
1340 (
1341 DatasourceId::JbossServiceXml,
1342 UnassembledReason::NotAPackage,
1343 ),
1344 (
1346 DatasourceId::AlpineApkArchive,
1347 UnassembledReason::BinaryArtifact,
1348 ),
1349 (DatasourceId::AndroidAab, UnassembledReason::BinaryArtifact),
1350 (DatasourceId::AndroidApk, UnassembledReason::BinaryArtifact),
1351 (DatasourceId::AppleDmg, UnassembledReason::BinaryArtifact),
1352 (DatasourceId::Axis2Mar, UnassembledReason::BinaryArtifact),
1353 (DatasourceId::ChromeCrx, UnassembledReason::BinaryArtifact),
1354 (
1355 DatasourceId::DebianOriginalSourceTarball,
1356 UnassembledReason::BinaryArtifact,
1357 ),
1358 (
1359 DatasourceId::DebianSourceMetadataTarball,
1360 UnassembledReason::BinaryArtifact,
1361 ),
1362 (
1363 DatasourceId::InstallshieldInstaller,
1364 UnassembledReason::BinaryArtifact,
1365 ),
1366 (DatasourceId::IosIpa, UnassembledReason::BinaryArtifact),
1367 (
1368 DatasourceId::IsoDiskImage,
1369 UnassembledReason::BinaryArtifact,
1370 ),
1371 (
1372 DatasourceId::JavaEarArchive,
1373 UnassembledReason::BinaryArtifact,
1374 ),
1375 (DatasourceId::JbossSar, UnassembledReason::BinaryArtifact),
1376 (
1377 DatasourceId::MicrosoftCabinet,
1378 UnassembledReason::BinaryArtifact,
1379 ),
1380 (DatasourceId::MozillaXpi, UnassembledReason::BinaryArtifact),
1381 (
1382 DatasourceId::NsisInstaller,
1383 UnassembledReason::BinaryArtifact,
1384 ),
1385 (
1386 DatasourceId::SharShellArchive,
1387 UnassembledReason::BinaryArtifact,
1388 ),
1389 (
1390 DatasourceId::SquashfsDiskImage,
1391 UnassembledReason::BinaryArtifact,
1392 ),
1393 (DatasourceId::GoBinary, UnassembledReason::BinaryArtifact),
1394 (
1395 DatasourceId::WindowsExecutable,
1396 UnassembledReason::BinaryArtifact,
1397 ),
1398 (DatasourceId::RustBinary, UnassembledReason::BinaryArtifact),
1399 (
1401 DatasourceId::DebianInstalledFilesList,
1402 UnassembledReason::SupplementaryMetadata,
1403 ),
1404 (
1405 DatasourceId::DebianInstalledMd5Sums,
1406 UnassembledReason::SupplementaryMetadata,
1407 ),
1408 (
1409 DatasourceId::DebianCopyright,
1410 UnassembledReason::SupplementaryMetadata,
1411 ),
1412 (
1413 DatasourceId::DebianCopyrightInPackage,
1414 UnassembledReason::SupplementaryMetadata,
1415 ),
1416 (
1417 DatasourceId::DebianCopyrightStandalone,
1418 UnassembledReason::SupplementaryMetadata,
1419 ),
1420 (
1421 DatasourceId::AntIvyDependenciesProperties,
1422 UnassembledReason::SupplementaryMetadata,
1423 ),
1424 (
1425 DatasourceId::NugetDirectoryBuildProps,
1426 UnassembledReason::SupplementaryMetadata,
1427 ),
1428 (
1429 DatasourceId::NugetDirectoryPackagesProps,
1430 UnassembledReason::SupplementaryMetadata,
1431 ),
1432 (
1433 DatasourceId::CitationCff,
1434 UnassembledReason::SupplementaryMetadata,
1435 ),
1436 (
1437 DatasourceId::PubliccodeYaml,
1438 UnassembledReason::SupplementaryMetadata,
1439 ),
1440 (
1441 DatasourceId::RpmPackageLicenses,
1442 UnassembledReason::SupplementaryMetadata,
1443 ),
1444 (
1445 DatasourceId::VcpkgConfigurationJson,
1446 UnassembledReason::SupplementaryMetadata,
1447 ),
1448 (
1449 DatasourceId::VcpkgLockJson,
1450 UnassembledReason::SupplementaryMetadata,
1451 ),
1452 (
1454 DatasourceId::ClojureDepsEdn,
1455 UnassembledReason::DependenciesOnlyNoIdentity,
1456 ),
1457];
1458
1459pub(super) fn is_unassembled_datasource(datasource_id: DatasourceId) -> bool {
1462 UNASSEMBLED_DATASOURCE_IDS
1463 .iter()
1464 .any(|(dsid, _)| *dsid == datasource_id)
1465}
1466
1467#[cfg(test)]
1468mod tests {
1469 use super::*;
1470 use std::collections::HashSet;
1471 use strum::IntoEnumIterator;
1472
1473 #[test]
1474 fn test_every_datasource_id_is_accounted_for() {
1475 let mut assembled: HashSet<DatasourceId> = HashSet::new();
1476 for config in ASSEMBLERS {
1477 for &dsid in config.datasource_ids {
1478 assembled.insert(dsid);
1479 }
1480 }
1481
1482 let unassembled: HashSet<DatasourceId> =
1483 UNASSEMBLED_DATASOURCE_IDS.iter().map(|(d, _)| *d).collect();
1484 assert_eq!(
1485 unassembled.len(),
1486 UNASSEMBLED_DATASOURCE_IDS.len(),
1487 "UNASSEMBLED_DATASOURCE_IDS lists a datasource more than once"
1488 );
1489
1490 let overlap: Vec<_> = assembled.intersection(&unassembled).collect();
1491 assert!(
1492 overlap.is_empty(),
1493 "Datasource IDs in BOTH ASSEMBLERS and UNASSEMBLED: {overlap:?}"
1494 );
1495
1496 let missing: Vec<_> = DatasourceId::iter()
1497 .filter(|dsid| !assembled.contains(dsid) && !unassembled.contains(dsid))
1498 .collect();
1499
1500 assert!(
1501 missing.is_empty(),
1502 "Datasource IDs in NEITHER ASSEMBLERS nor UNASSEMBLED: {missing:?}\n\
1503 Add each to an AssemblerConfig in ASSEMBLERS, or to UNASSEMBLED_DATASOURCE_IDS."
1504 );
1505 }
1506
1507 fn pass(id: PostAssemblyPassId) -> &'static PostAssemblyPass {
1508 POST_ASSEMBLY_PASSES
1509 .iter()
1510 .find(|pass| pass.id == id)
1511 .expect("every PostAssemblyPassId is registered")
1512 }
1513
1514 #[test]
1515 fn test_every_post_assembly_pass_id_is_registered_exactly_once() {
1516 for id in PostAssemblyPassId::iter() {
1517 let count = POST_ASSEMBLY_PASSES
1518 .iter()
1519 .filter(|pass| pass.id == id)
1520 .count();
1521 assert_eq!(
1522 count, 1,
1523 "post-assembly pass {id:?} should be registered exactly once"
1524 );
1525 }
1526 assert_eq!(
1527 POST_ASSEMBLY_PASSES.len(),
1528 PostAssemblyPassId::iter().count(),
1529 "POST_ASSEMBLY_PASSES contains passes not in PostAssemblyPassId"
1530 );
1531 }
1532
1533 #[test]
1534 fn test_every_workspace_marker_has_exactly_one_detector() {
1535 for marker in [
1536 WorkspaceMarker::NpmWorkspace,
1537 WorkspaceMarker::CargoWorkspace,
1538 WorkspaceMarker::MixUmbrella,
1539 WorkspaceMarker::MavenReactor,
1540 WorkspaceMarker::GradleMultiProject,
1541 WorkspaceMarker::UvWorkspace,
1542 WorkspaceMarker::DartWorkspace,
1543 ] {
1544 let count = MARKER_DETECTORS
1545 .iter()
1546 .filter(|detector| detector.marker == marker)
1547 .count();
1548 assert_eq!(
1549 count, 1,
1550 "workspace marker {marker:?} should have exactly one detector"
1551 );
1552 }
1553 }
1554
1555 #[test]
1556 fn test_post_assembly_passes_skip_irrelevant_inputs() {
1557 let inputs = PostAssemblyInputs::default();
1558
1559 for post_assembly_pass in POST_ASSEMBLY_PASSES {
1560 assert!(
1561 !(post_assembly_pass.should_run)(&inputs),
1562 "{:?} should skip when no relevant inputs are present",
1563 post_assembly_pass.id
1564 );
1565 }
1566 }
1567
1568 #[test]
1569 fn test_npm_workspace_inputs_only_run_npm_passes() {
1570 let inputs = PostAssemblyInputs {
1571 package_types: HashSet::from([PackageType::Npm]),
1572 file_datasource_ids: HashSet::from([DatasourceId::NpmPackageJson]),
1573 markers: HashSet::from([WorkspaceMarker::NpmWorkspace]),
1574 };
1575
1576 let runnable: HashSet<_> = POST_ASSEMBLY_PASSES
1577 .iter()
1578 .filter(|post_assembly_pass| (post_assembly_pass.should_run)(&inputs))
1579 .map(|post_assembly_pass| post_assembly_pass.id)
1580 .collect();
1581
1582 assert_eq!(
1583 runnable,
1584 HashSet::from([
1585 PostAssemblyPassId::NpmResourceAssign,
1586 PostAssemblyPassId::NpmWorkspaceMerge,
1587 ])
1588 );
1589 }
1590
1591 #[test]
1592 fn test_cargo_workspace_merge_requires_workspace_markers() {
1593 let without_markers = PostAssemblyInputs {
1594 package_types: HashSet::from([PackageType::Cargo]),
1595 file_datasource_ids: HashSet::from([DatasourceId::CargoToml]),
1596 markers: HashSet::new(),
1597 };
1598
1599 assert!(!(pass(PostAssemblyPassId::CargoWorkspaceMerge).should_run)(
1600 &without_markers
1601 ));
1602
1603 let with_markers = PostAssemblyInputs {
1604 markers: HashSet::from([WorkspaceMarker::CargoWorkspace]),
1605 ..without_markers
1606 };
1607
1608 assert!((pass(PostAssemblyPassId::CargoWorkspaceMerge).should_run)(
1609 &with_markers
1610 ));
1611 }
1612
1613 #[test]
1614 fn test_mix_umbrella_merge_requires_umbrella_markers() {
1615 let without_markers = PostAssemblyInputs {
1616 package_types: HashSet::new(),
1617 file_datasource_ids: HashSet::from([DatasourceId::HexMixExs]),
1618 markers: HashSet::new(),
1619 };
1620
1621 assert!(!(pass(PostAssemblyPassId::MixUmbrellaMerge).should_run)(
1622 &without_markers
1623 ));
1624
1625 let with_markers = PostAssemblyInputs {
1626 markers: HashSet::from([WorkspaceMarker::MixUmbrella]),
1627 ..without_markers
1628 };
1629
1630 assert!((pass(PostAssemblyPassId::MixUmbrellaMerge).should_run)(
1631 &with_markers
1632 ));
1633 }
1634}