1use crate::models::{DatasourceId, FileInfo, Package, TopLevelDependency};
2use strum::EnumIter;
3
4use super::{
5 AssemblerConfig, AssemblyMode, DirectoryMergeOutput, cargo_resource_assign,
6 cargo_workspace_merge, composer_resource_assign, conda_rootfs_merge, file_ref_resolve,
7 hackage_merge, npm_resource_assign, npm_workspace_merge, nuget_cpm_resolve,
8 python_requirements_assign, ruby_resource_assign, swift_merge,
9};
10
11#[derive(Clone, Copy)]
12pub(super) enum SpecialDirectoryMergerKind {
13 Skip,
14 Hackage,
15}
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIter)]
18pub(super) enum PostAssemblyPassKind {
19 SwiftMerge,
20 CondaRootfsMerge,
21 NpmResourceAssign,
22 PythonRequirementsAssign,
23 FileReferenceResolve,
24 RpmYumdbMerge,
25 NpmWorkspaceMerge,
26 CargoWorkspaceMerge,
27 NugetCpmResolve,
28 CargoResourceAssign,
29 ComposerResourceAssign,
30 RubyResourceAssign,
31}
32
33pub(super) fn special_directory_merger_for(
34 config_key: DatasourceId,
35) -> Option<SpecialDirectoryMergerKind> {
36 match config_key {
37 DatasourceId::HackageCabal => Some(SpecialDirectoryMergerKind::Hackage),
38 DatasourceId::SwiftPackageManifestJson => Some(SpecialDirectoryMergerKind::Skip),
39 _ => None,
40 }
41}
42
43pub(super) static POST_ASSEMBLY_PASSES: &[PostAssemblyPassKind] = &[
44 PostAssemblyPassKind::SwiftMerge,
45 PostAssemblyPassKind::CondaRootfsMerge,
46 PostAssemblyPassKind::NpmResourceAssign,
47 PostAssemblyPassKind::PythonRequirementsAssign,
48 PostAssemblyPassKind::FileReferenceResolve,
49 PostAssemblyPassKind::RpmYumdbMerge,
50 PostAssemblyPassKind::NpmWorkspaceMerge,
51 PostAssemblyPassKind::CargoWorkspaceMerge,
52 PostAssemblyPassKind::NugetCpmResolve,
53 PostAssemblyPassKind::CargoResourceAssign,
54 PostAssemblyPassKind::ComposerResourceAssign,
55 PostAssemblyPassKind::RubyResourceAssign,
56];
57
58pub(super) fn run_post_assembly_passes(
59 files: &mut [FileInfo],
60 packages: &mut Vec<Package>,
61 dependencies: &mut Vec<TopLevelDependency>,
62) {
63 for pass in POST_ASSEMBLY_PASSES {
64 pass.run(files, packages, dependencies);
65 }
66}
67
68impl SpecialDirectoryMergerKind {
69 pub(super) fn run(
70 self,
71 files: &[FileInfo],
72 file_indices: &[usize],
73 ) -> Vec<DirectoryMergeOutput> {
74 match self {
75 Self::Skip => Vec::new(),
76 Self::Hackage => hackage_merge::assemble_hackage_packages(files, file_indices),
77 }
78 }
79}
80
81impl PostAssemblyPassKind {
82 fn run(
83 self,
84 files: &mut [FileInfo],
85 packages: &mut Vec<Package>,
86 dependencies: &mut Vec<TopLevelDependency>,
87 ) {
88 match self {
89 Self::SwiftMerge => swift_merge::assemble_swift_packages(files, packages, dependencies),
90 Self::CondaRootfsMerge => {
91 conda_rootfs_merge::merge_conda_rootfs_metadata(files, packages, dependencies)
92 }
93 Self::NpmResourceAssign => {
94 npm_resource_assign::assign_npm_package_resources(files, packages)
95 }
96 Self::PythonRequirementsAssign => {
97 python_requirements_assign::assign_python_requirements_to_projects(
98 files,
99 packages,
100 dependencies,
101 )
102 }
103 Self::FileReferenceResolve => {
104 file_ref_resolve::resolve_file_references(files, packages, dependencies)
105 }
106 Self::RpmYumdbMerge => file_ref_resolve::merge_rpm_yumdb_metadata(files, packages),
107 Self::NpmWorkspaceMerge => {
108 npm_workspace_merge::assemble_npm_workspaces(files, packages, dependencies)
109 }
110 Self::CargoWorkspaceMerge => {
111 cargo_workspace_merge::assemble_cargo_workspaces(files, packages, dependencies)
112 }
113 Self::NugetCpmResolve => {
114 nuget_cpm_resolve::resolve_nuget_cpm_versions(files, dependencies)
115 }
116 Self::CargoResourceAssign => {
117 cargo_resource_assign::assign_cargo_package_resources(files, packages)
118 }
119 Self::ComposerResourceAssign => {
120 composer_resource_assign::assign_composer_package_resources(files, packages)
121 }
122 Self::RubyResourceAssign => {
123 ruby_resource_assign::assign_ruby_package_resources(files, packages)
124 }
125 }
126 }
127}
128
129pub static ASSEMBLERS: &[AssemblerConfig] = &[
130 AssemblerConfig {
136 datasource_ids: &[
137 DatasourceId::BunLock,
138 DatasourceId::BunLockb,
139 DatasourceId::NpmPackageJson,
140 DatasourceId::NpmPackageLockJson,
141 DatasourceId::YarnLock,
142 DatasourceId::PnpmLockYaml,
143 DatasourceId::PnpmWorkspaceYaml,
144 ],
145 sibling_file_patterns: &[
146 "package.json",
147 "bun.lock",
148 "bun.lockb",
149 ".package-lock.json",
150 "package-lock.json",
151 ".npm-shrinkwrap.json",
152 "npm-shrinkwrap.json",
153 "yarn.lock",
154 "pnpm-lock.yaml",
155 "shrinkwrap.yaml",
156 "pnpm-workspace.yaml",
157 ],
158 mode: AssemblyMode::SiblingMerge,
159 },
160 AssemblerConfig {
162 datasource_ids: &[DatasourceId::CargoToml, DatasourceId::CargoLock],
163 sibling_file_patterns: &["Cargo.toml", "Cargo.lock"],
164 mode: AssemblyMode::SiblingMerge,
165 },
166 AssemblerConfig {
168 datasource_ids: &[
169 DatasourceId::CocoapodsPodspec,
170 DatasourceId::CocoapodsPodspecJson,
171 DatasourceId::CocoapodsPodfile,
172 DatasourceId::CocoapodsPodfileLock,
173 ],
174 sibling_file_patterns: &["*.podspec", "*.podspec.json", "Podfile", "Podfile.lock"],
175 mode: AssemblyMode::SiblingMerge,
176 },
177 AssemblerConfig {
179 datasource_ids: &[DatasourceId::PhpComposerJson, DatasourceId::PhpComposerLock],
180 sibling_file_patterns: &[
181 "*composer.json",
182 "composer.*.json",
183 "*composer.lock",
184 "composer.*.lock",
185 ],
186 mode: AssemblyMode::SiblingMerge,
187 },
188 AssemblerConfig {
190 datasource_ids: &[
191 DatasourceId::GoMod,
192 DatasourceId::GoModGraph,
193 DatasourceId::GoSum,
194 DatasourceId::GoWork,
195 DatasourceId::Godeps,
196 ],
197 sibling_file_patterns: &[
198 "go.mod",
199 "go.work",
200 "go.mod.graph",
201 "go.modgraph",
202 "go.sum",
203 "Godeps.json",
204 ],
205 mode: AssemblyMode::SiblingMerge,
206 },
207 AssemblerConfig {
209 datasource_ids: &[DatasourceId::PubspecYaml, DatasourceId::PubspecLock],
210 sibling_file_patterns: &["pubspec.yaml", "pubspec.lock"],
211 mode: AssemblyMode::SiblingMerge,
212 },
213 AssemblerConfig {
215 datasource_ids: &[DatasourceId::PixiToml, DatasourceId::PixiLock],
216 sibling_file_patterns: &["pixi.toml", "pixi.lock"],
217 mode: AssemblyMode::SiblingMerge,
218 },
219 AssemblerConfig {
220 datasource_ids: &[DatasourceId::NixFlakeNix, DatasourceId::NixFlakeLock],
221 sibling_file_patterns: &["flake.nix", "flake.lock"],
222 mode: AssemblyMode::SiblingMerge,
223 },
224 AssemblerConfig {
225 datasource_ids: &[DatasourceId::NixDefaultNix],
226 sibling_file_patterns: &["default.nix"],
227 mode: AssemblyMode::OnePerPackageData,
228 },
229 AssemblerConfig {
231 datasource_ids: &[DatasourceId::HelmChartYaml, DatasourceId::HelmChartLock],
232 sibling_file_patterns: &["Chart.yaml", "Chart.lock"],
233 mode: AssemblyMode::SiblingMerge,
234 },
235 AssemblerConfig {
236 datasource_ids: &[
237 DatasourceId::HackageCabal,
238 DatasourceId::HackageCabalProject,
239 DatasourceId::HackageStackYaml,
240 ],
241 sibling_file_patterns: &["*.cabal", "cabal.project", "stack.yaml"],
242 mode: AssemblyMode::SiblingMerge,
243 },
244 AssemblerConfig {
246 datasource_ids: &[
247 DatasourceId::ChefCookbookMetadataJson,
248 DatasourceId::ChefCookbookMetadataRb,
249 ],
250 sibling_file_patterns: &["metadata.json", "metadata.rb"],
251 mode: AssemblyMode::SiblingMerge,
252 },
253 AssemblerConfig {
255 datasource_ids: &[
256 DatasourceId::ConanConanFilePy,
257 DatasourceId::ConanConanFileTxt,
258 DatasourceId::ConanLock,
259 DatasourceId::ConanConanDataYml,
260 ],
261 sibling_file_patterns: &[
262 "conanfile.py",
263 "conanfile.txt",
264 "conan.lock",
265 "conandata.yml",
266 ],
267 mode: AssemblyMode::SiblingMerge,
268 },
269 AssemblerConfig {
271 datasource_ids: &[
272 DatasourceId::MavenPom,
273 DatasourceId::MavenPomProperties,
274 DatasourceId::JavaJarManifest,
275 DatasourceId::JavaOsgiManifest,
276 ],
277 sibling_file_patterns: &[
278 "pom.xml",
279 "*.pom",
280 "pom.properties",
281 "**/META-INF/MANIFEST.MF",
282 ],
283 mode: AssemblyMode::SiblingMerge,
284 },
285 AssemblerConfig {
286 datasource_ids: &[DatasourceId::PypiWheel, DatasourceId::PypiPipOriginJson],
287 sibling_file_patterns: &["*.whl", "origin.json"],
288 mode: AssemblyMode::SiblingMerge,
289 },
290 AssemblerConfig {
292 datasource_ids: &[
293 DatasourceId::PypiPyprojectToml,
294 DatasourceId::PypiSetupPy,
295 DatasourceId::PypiSetupCfg,
296 DatasourceId::PypiWheel,
297 DatasourceId::PypiWheelMetadata,
298 DatasourceId::PypiEgg,
299 DatasourceId::PypiJson,
300 DatasourceId::PypiSdistPkginfo,
301 DatasourceId::PypiInspectDeplock,
302 DatasourceId::PipRequirements,
303 DatasourceId::PypiPoetryLock,
304 DatasourceId::PypiPylockToml,
305 DatasourceId::PypiUvLock,
306 DatasourceId::Pipfile,
307 DatasourceId::PipfileLock,
308 ],
309 sibling_file_patterns: &[
310 "pyproject.toml",
311 "setup.py",
312 "setup.cfg",
313 "PKG-INFO",
314 "METADATA",
315 "pypi.json",
316 "pip-inspect.deplock",
317 "requirements*.txt",
318 "Pipfile",
319 "Pipfile.lock",
320 "poetry.lock",
321 "pylock.toml",
322 "pylock.*.toml",
323 "uv.lock",
324 ],
325 mode: AssemblyMode::SiblingMerge,
326 },
327 AssemblerConfig {
328 datasource_ids: &[DatasourceId::DenoJson, DatasourceId::DenoLock],
329 sibling_file_patterns: &["deno.json", "deno.jsonc", "deno.lock"],
330 mode: AssemblyMode::SiblingMerge,
331 },
332 AssemblerConfig {
334 datasource_ids: &[
335 DatasourceId::GemArchiveExtracted,
336 DatasourceId::Gemspec,
337 DatasourceId::Gemfile,
338 DatasourceId::GemfileLock,
339 DatasourceId::GemArchive,
340 ],
341 sibling_file_patterns: &[
342 "metadata.gz-extract",
343 "**/data.gz-extract/*.gemspec",
344 "**/data.gz-extract/Gemfile",
345 "**/data.gz-extract/Gemfile.lock",
346 "*.gemspec",
347 "Gemfile",
348 "Gemfile.lock",
349 ],
350 mode: AssemblyMode::SiblingMerge,
351 },
352 AssemblerConfig {
354 datasource_ids: &[
355 DatasourceId::CondaMetaYaml,
356 DatasourceId::CondaYaml,
357 DatasourceId::CondaMetaJson,
358 ],
359 sibling_file_patterns: &[
360 "meta.yaml",
361 "meta.yml",
362 "environment.yml",
363 "environment.yaml",
364 "conda.yaml",
365 "conda.yml",
366 "*conda*.yaml",
367 "*conda*.yml",
368 "env.yaml",
369 "env.yml",
370 "*env*.yaml",
371 "*env*.yml",
372 "*environment*.yaml",
373 "*environment*.yml",
374 "*.json",
375 ],
376 mode: AssemblyMode::SiblingMerge,
377 },
378 AssemblerConfig {
380 datasource_ids: &[DatasourceId::RpmSpecfile],
381 sibling_file_patterns: &["*.spec"],
382 mode: AssemblyMode::SiblingMerge,
383 },
384 AssemblerConfig {
386 datasource_ids: &[
387 DatasourceId::DebianControlInSource,
388 DatasourceId::DebianCopyright,
389 ],
390 sibling_file_patterns: &["**/debian/control", "**/debian/copyright"],
391 mode: AssemblyMode::SiblingMerge,
392 },
393 AssemblerConfig {
395 datasource_ids: &[DatasourceId::BuildGradle, DatasourceId::GradleLockfile],
396 sibling_file_patterns: &["build.gradle", "build.gradle.kts", "gradle.lockfile"],
397 mode: AssemblyMode::SiblingMerge,
398 },
399 AssemblerConfig {
400 datasource_ids: &[DatasourceId::GradleModule],
401 sibling_file_patterns: &["*.module"],
402 mode: AssemblyMode::OnePerPackageData,
403 },
404 AssemblerConfig {
406 datasource_ids: &[
407 DatasourceId::CpanMetaJson,
408 DatasourceId::CpanMetaYml,
409 DatasourceId::CpanManifest,
410 DatasourceId::CpanDistIni,
411 DatasourceId::CpanMakefile,
412 ],
413 sibling_file_patterns: &[
414 "META.json",
415 "META.yml",
416 "MANIFEST",
417 "dist.ini",
418 "Makefile.PL",
419 ],
420 mode: AssemblyMode::SiblingMerge,
421 },
422 AssemblerConfig {
424 datasource_ids: &[
425 DatasourceId::NugetCsproj,
426 DatasourceId::NugetFsproj,
427 DatasourceId::NugetNuspec,
428 DatasourceId::NugetNupkg,
429 DatasourceId::NugetProjectJson,
430 DatasourceId::NugetProjectLockJson,
431 DatasourceId::NugetPackagesConfig,
432 DatasourceId::NugetPackagesLock,
433 DatasourceId::NugetVbproj,
434 ],
435 sibling_file_patterns: &[
436 "*.csproj",
437 "*.fsproj",
438 "*.nuspec",
439 "*.nupkg",
440 "project.json",
441 "project.lock.json",
442 "packages.config",
443 "packages.lock.json",
444 "*.packages.lock.json",
445 "*.vbproj",
446 ],
447 mode: AssemblyMode::SiblingMerge,
448 },
449 AssemblerConfig {
450 datasource_ids: &[DatasourceId::NugetDepsJson],
451 sibling_file_patterns: &["*.deps.json"],
452 mode: AssemblyMode::OnePerPackageData,
453 },
454 AssemblerConfig {
456 datasource_ids: &[
457 DatasourceId::SwiftPackageManifestJson,
458 DatasourceId::SwiftPackageResolved,
459 DatasourceId::SwiftPackageShowDependencies,
460 ],
461 sibling_file_patterns: &[
462 "Package.swift.json",
463 "Package.swift.deplock",
464 "Package.resolved",
465 ".package.resolved",
466 "swift-show-dependencies.deplock",
467 ],
468 mode: AssemblyMode::SiblingMerge,
469 },
470 AssemblerConfig {
477 datasource_ids: &[DatasourceId::BowerJson],
478 sibling_file_patterns: &["bower.json"],
479 mode: AssemblyMode::SiblingMerge,
480 },
481 AssemblerConfig {
483 datasource_ids: &[DatasourceId::CranDescription],
484 sibling_file_patterns: &["DESCRIPTION"],
485 mode: AssemblyMode::SiblingMerge,
486 },
487 AssemblerConfig {
489 datasource_ids: &[DatasourceId::FreebsdCompactManifest],
490 sibling_file_patterns: &["+COMPACT_MANIFEST"],
491 mode: AssemblyMode::SiblingMerge,
492 },
493 AssemblerConfig {
495 datasource_ids: &[DatasourceId::HaxelibJson],
496 sibling_file_patterns: &["haxelib.json"],
497 mode: AssemblyMode::SiblingMerge,
498 },
499 AssemblerConfig {
500 datasource_ids: &[DatasourceId::Gitmodules],
501 sibling_file_patterns: &[".gitmodules"],
502 mode: AssemblyMode::SiblingMerge,
503 },
504 AssemblerConfig {
506 datasource_ids: &[DatasourceId::OpamFile],
507 sibling_file_patterns: &["opam", "*.opam"],
508 mode: AssemblyMode::SiblingMerge,
509 },
510 AssemblerConfig {
512 datasource_ids: &[DatasourceId::RpmMarinerManifest],
513 sibling_file_patterns: &["*.rpm.manifest"],
514 mode: AssemblyMode::SiblingMerge,
515 },
516 AssemblerConfig {
517 datasource_ids: &[DatasourceId::RpmYumdb],
518 sibling_file_patterns: &["**/var/lib/yum/yumdb/*/*/from_repo"],
519 mode: AssemblyMode::OnePerPackageData,
520 },
521 AssemblerConfig {
523 datasource_ids: &[DatasourceId::MicrosoftUpdateManifestMum],
524 sibling_file_patterns: &["*.mum"],
525 mode: AssemblyMode::SiblingMerge,
526 },
527 AssemblerConfig {
529 datasource_ids: &[DatasourceId::AutotoolsConfigure],
530 sibling_file_patterns: &["configure", "configure.ac"],
531 mode: AssemblyMode::SiblingMerge,
532 },
533 AssemblerConfig {
535 datasource_ids: &[DatasourceId::BazelBuild],
536 sibling_file_patterns: &["BUILD"],
537 mode: AssemblyMode::SiblingMerge,
538 },
539 AssemblerConfig {
540 datasource_ids: &[DatasourceId::BazelModule],
541 sibling_file_patterns: &["MODULE.bazel"],
542 mode: AssemblyMode::OnePerPackageData,
543 },
544 AssemblerConfig {
546 datasource_ids: &[DatasourceId::BuckFile, DatasourceId::BuckMetadata],
547 sibling_file_patterns: &["BUCK", "METADATA.bzl", ".buckconfig"],
548 mode: AssemblyMode::SiblingMerge,
549 },
550 AssemblerConfig {
552 datasource_ids: &[DatasourceId::AntIvyXml],
553 sibling_file_patterns: &["ivy.xml"],
554 mode: AssemblyMode::SiblingMerge,
555 },
556 AssemblerConfig {
558 datasource_ids: &[DatasourceId::MeteorPackage],
559 sibling_file_patterns: &["package.js"],
560 mode: AssemblyMode::SiblingMerge,
561 },
562 AssemblerConfig {
566 datasource_ids: &[DatasourceId::AlpineInstalledDb],
567 sibling_file_patterns: &["installed"],
568 mode: AssemblyMode::OnePerPackageData,
569 },
570 AssemblerConfig {
571 datasource_ids: &[DatasourceId::AlpineApkbuild],
572 sibling_file_patterns: &["APKBUILD"],
573 mode: AssemblyMode::SiblingMerge,
574 },
575 AssemblerConfig {
577 datasource_ids: &[
578 DatasourceId::RpmInstalledDatabaseBdb,
579 DatasourceId::RpmInstalledDatabaseNdb,
580 DatasourceId::RpmInstalledDatabaseSqlite,
581 ],
582 sibling_file_patterns: &["Packages", "Packages.db", "rpmdb.sqlite"],
583 mode: AssemblyMode::OnePerPackageData,
584 },
585 AssemblerConfig {
587 datasource_ids: &[
588 DatasourceId::DebianInstalledStatusDb,
589 DatasourceId::DebianDistrolessInstalledDb,
590 ],
591 sibling_file_patterns: &["status"],
592 mode: AssemblyMode::OnePerPackageData,
593 },
594 AssemblerConfig {
595 datasource_ids: &[
596 DatasourceId::DebianControlExtractedDeb,
597 DatasourceId::DebianMd5SumsInExtractedDeb,
598 ],
599 sibling_file_patterns: &["control", "md5sums"],
600 mode: AssemblyMode::SiblingMerge,
601 },
602 AssemblerConfig {
603 datasource_ids: &[DatasourceId::AboutFile],
604 sibling_file_patterns: &["*.ABOUT"],
605 mode: AssemblyMode::OnePerPackageData,
606 },
607];
608
609pub static UNASSEMBLED_DATASOURCE_IDS: &[DatasourceId] = &[
615 DatasourceId::Readme,
617 DatasourceId::EtcOsRelease,
618 DatasourceId::AlpineApkArchive,
620 DatasourceId::AndroidAarLibrary,
621 DatasourceId::AndroidApk,
622 DatasourceId::AppleDmg,
623 DatasourceId::Axis2Mar,
624 DatasourceId::ChromeCrx,
625 DatasourceId::DebianDeb,
626 DatasourceId::DebianOriginalSourceTarball,
627 DatasourceId::DebianSourceMetadataTarball,
628 DatasourceId::InstallshieldInstaller,
629 DatasourceId::IosIpa,
630 DatasourceId::IsoDiskImage,
631 DatasourceId::JavaEarArchive,
632 DatasourceId::JavaJar,
633 DatasourceId::JavaWarArchive,
634 DatasourceId::JbossSar,
635 DatasourceId::MicrosoftCabinet,
636 DatasourceId::MozillaXpi,
637 DatasourceId::NsisInstaller,
638 DatasourceId::RpmArchive,
639 DatasourceId::SharShellArchive,
640 DatasourceId::SquashfsDiskImage,
641 DatasourceId::ArchAurinfo,
643 DatasourceId::ArchPkginfo,
644 DatasourceId::ArchSrcinfo,
645 DatasourceId::Axis2ModuleXml,
646 DatasourceId::ClojureDepsEdn,
647 DatasourceId::ClojureProjectClj,
648 DatasourceId::DebianInstalledFilesList,
649 DatasourceId::DebianInstalledMd5Sums,
650 DatasourceId::DebianSourceControlDsc,
651 DatasourceId::Dockerfile,
652 DatasourceId::HexMixLock,
653 DatasourceId::JavaEarApplicationXml,
654 DatasourceId::JavaWarWebXml,
655 DatasourceId::JbossServiceXml,
656 DatasourceId::MesonBuild,
657 DatasourceId::NugetDirectoryBuildProps,
658 DatasourceId::NugetDirectoryPackagesProps,
659 DatasourceId::RpmPackageLicenses,
660 DatasourceId::SbtBuildSbt,
661 DatasourceId::VcpkgJson,
662];
663
664#[cfg(test)]
665mod tests {
666 use super::*;
667 use std::collections::HashSet;
668 use strum::IntoEnumIterator;
669
670 #[test]
671 fn test_every_datasource_id_is_accounted_for() {
672 let mut assembled: HashSet<DatasourceId> = HashSet::new();
673 for config in ASSEMBLERS {
674 for &dsid in config.datasource_ids {
675 assembled.insert(dsid);
676 }
677 }
678
679 let unassembled: HashSet<DatasourceId> =
680 UNASSEMBLED_DATASOURCE_IDS.iter().copied().collect();
681
682 let overlap: Vec<_> = assembled.intersection(&unassembled).collect();
683 assert!(
684 overlap.is_empty(),
685 "Datasource IDs in BOTH ASSEMBLERS and UNASSEMBLED: {overlap:?}"
686 );
687
688 let missing: Vec<_> = DatasourceId::iter()
689 .filter(|dsid| !assembled.contains(dsid) && !unassembled.contains(dsid))
690 .collect();
691
692 assert!(
693 missing.is_empty(),
694 "Datasource IDs in NEITHER ASSEMBLERS nor UNASSEMBLED: {missing:?}\n\
695 Add each to an AssemblerConfig in ASSEMBLERS, or to UNASSEMBLED_DATASOURCE_IDS."
696 );
697 }
698
699 #[test]
700 fn test_post_assembly_passes_are_unique() {
701 let unique: HashSet<PostAssemblyPassKind> = POST_ASSEMBLY_PASSES.iter().copied().collect();
702
703 assert_eq!(
704 unique.len(),
705 POST_ASSEMBLY_PASSES.len(),
706 "POST_ASSEMBLY_PASSES contains duplicate entries"
707 );
708 }
709
710 #[test]
711 fn test_every_post_assembly_pass_kind_is_registered_once() {
712 let registered: HashSet<PostAssemblyPassKind> =
713 POST_ASSEMBLY_PASSES.iter().copied().collect();
714
715 let missing: Vec<_> = PostAssemblyPassKind::iter()
716 .filter(|pass| !registered.contains(pass))
717 .collect();
718
719 assert!(
720 missing.is_empty(),
721 "Post-assembly pass variants not registered in POST_ASSEMBLY_PASSES: {missing:?}"
722 );
723
724 for pass in PostAssemblyPassKind::iter() {
725 let count = POST_ASSEMBLY_PASSES
726 .iter()
727 .filter(|registered| **registered == pass)
728 .count();
729 assert_eq!(
730 count, 1,
731 "Post-assembly pass {pass:?} should be registered exactly once"
732 );
733 }
734 }
735}