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