1mod about;
2#[cfg(test)]
3mod about_scan_test;
4#[cfg(test)]
5mod about_test;
6mod alpine;
7#[cfg(test)]
8mod alpine_scan_test;
9mod arch;
10#[cfg(test)]
11mod arch_scan_test;
12#[cfg(test)]
13mod arch_test;
14mod autotools;
15#[cfg(test)]
16mod autotools_test;
17mod bazel;
18#[cfg(test)]
19mod bazel_module_test;
20#[cfg(test)]
21mod bazel_test;
22mod bower;
23#[cfg(test)]
24mod bower_scan_test;
25#[cfg(test)]
26mod bower_test;
27mod buck;
28#[cfg(test)]
29mod buck_test;
30mod bun_lock;
31#[cfg(test)]
32mod bun_lock_test;
33mod bun_lockb;
34#[cfg(test)]
35mod bun_lockb_test;
36mod cargo;
37mod cargo_lock;
38#[cfg(test)]
39mod cargo_lock_test;
40#[cfg(test)]
41mod cargo_scan_test;
42#[cfg(test)]
43mod cargo_test;
44mod chef;
45#[cfg(test)]
46mod chef_scan_test;
47#[cfg(test)]
48mod chef_test;
49mod citation;
50#[cfg(test)]
51mod citation_test;
52mod clojure;
53#[cfg(test)]
54mod clojure_test;
55#[cfg(test)]
56mod cocoapods_scan_test;
57pub(crate) mod compiled_binary;
58mod composer;
59#[cfg(test)]
60mod composer_scan_test;
61#[cfg(test)]
62mod composer_test;
63mod conan;
64mod conan_data;
65#[cfg(test)]
66mod conan_data_test;
67#[cfg(test)]
68mod conan_scan_test;
69#[cfg(test)]
70mod conan_test;
71mod conda;
72mod conda_meta_json;
73#[cfg(test)]
74mod conda_meta_json_test;
75#[cfg(test)]
76mod conda_scan_test;
77#[cfg(test)]
78mod conda_test;
79mod cpan;
80mod cpan_dist_ini;
81#[cfg(test)]
82mod cpan_dist_ini_test;
83mod cpan_makefile_pl;
84#[cfg(test)]
85mod cpan_makefile_pl_test;
86#[cfg(test)]
87mod cpan_scan_test;
88#[cfg(test)]
89mod cpan_test;
90mod cran;
91#[cfg(test)]
92mod cran_scan_test;
93#[cfg(test)]
94mod cran_test;
95mod dart;
96#[cfg(test)]
97mod dart_scan_test;
98#[cfg(test)]
99mod dart_test;
100mod debian;
101mod deno;
102mod deno_lock;
103#[cfg(test)]
104mod deno_lock_test;
105#[cfg(test)]
106mod deno_scan_test;
107#[cfg(test)]
108mod deno_test;
109mod docker;
110#[cfg(test)]
111mod docker_scan_test;
112#[cfg(test)]
113mod docker_test;
114mod freebsd;
115#[cfg(test)]
116mod freebsd_scan_test;
117#[cfg(test)]
118mod freebsd_test;
119mod gitmodules;
120#[cfg(test)]
121mod gitmodules_scan_test;
122mod go;
123mod go_mod_graph;
124#[cfg(test)]
125mod go_scan_test;
126#[cfg(test)]
127mod go_test;
128#[cfg(test)]
129mod go_work_test;
130#[cfg(all(test, feature = "golden-tests"))]
131pub(crate) mod golden_test_utils;
132mod gradle;
133mod gradle_lock;
134#[cfg(test)]
135mod gradle_lock_test;
136mod gradle_module;
137#[cfg(test)]
138mod gradle_module_scan_test;
139#[cfg(test)]
140mod gradle_module_test;
141#[cfg(test)]
142mod gradle_scan_test;
143mod hackage;
144#[cfg(test)]
145mod hackage_scan_test;
146#[cfg(test)]
147mod hackage_test;
148mod haxe;
149#[cfg(test)]
150mod haxe_scan_test;
151#[cfg(test)]
152mod haxe_test;
153mod helm;
154#[cfg(test)]
155mod helm_scan_test;
156#[cfg(test)]
157mod helm_test;
158mod hex_lock;
159#[cfg(test)]
160mod hex_lock_test;
161mod julia;
162#[cfg(test)]
163mod julia_test;
164mod license_normalization;
165mod maven;
166#[cfg(test)]
167mod maven_scan_test;
168#[cfg(test)]
169mod maven_test;
170mod meson;
171#[cfg(test)]
172mod meson_scan_test;
173#[cfg(test)]
174mod meson_test;
175pub mod metadata;
176mod microsoft_update_manifest;
177#[cfg(test)]
178mod microsoft_update_manifest_test;
179mod misc;
180#[cfg(test)]
181mod misc_test;
182mod nix;
183#[cfg(test)]
184mod nix_scan_test;
185#[cfg(test)]
186mod nix_test;
187mod npm;
188mod npm_lock;
189#[cfg(test)]
190mod npm_lock_test;
191#[cfg(test)]
192mod npm_scan_test;
193#[cfg(test)]
194mod npm_test;
195mod npm_workspace;
196#[cfg(test)]
197mod npm_workspace_test;
198mod nuget;
199mod opam;
200#[cfg(test)]
201mod opam_scan_test;
202mod os_release;
203#[cfg(test)]
204mod os_release_test;
205#[cfg(test)]
206mod osgi_test;
207mod pep508;
208mod pip_inspect_deplock;
209#[cfg(test)]
210mod pip_inspect_deplock_test;
211mod pipfile_lock;
212#[cfg(test)]
213mod pipfile_lock_test;
214mod pixi;
215#[cfg(test)]
216mod pixi_scan_test;
217#[cfg(test)]
218mod pixi_test;
219mod pnpm_lock;
220#[cfg(test)]
221mod pnpm_lock_test;
222mod podfile;
223mod podfile_lock;
224#[cfg(test)]
225mod podfile_lock_test;
226mod podspec;
227mod podspec_json;
228#[cfg(test)]
229mod podspec_json_test;
230mod poetry_lock;
231#[cfg(test)]
232mod poetry_lock_test;
233mod publiccode;
234#[cfg(test)]
235mod publiccode_test;
236mod pylock_toml;
237#[cfg(test)]
238mod pylock_toml_test;
239mod python;
240mod readme;
241#[cfg(test)]
242mod readme_test;
243mod requirements_txt;
244#[cfg(test)]
245mod requirements_txt_test;
246pub(crate) mod rfc822;
247mod rpm_db;
248mod rpm_db_native;
249#[cfg(test)]
250mod rpm_db_scan_test;
251mod rpm_license_files;
252#[cfg(test)]
253mod rpm_license_files_test;
254mod rpm_mariner_manifest;
255#[cfg(test)]
256mod rpm_mariner_manifest_test;
257mod rpm_parser;
258#[cfg(test)]
259mod rpm_scan_test;
260mod rpm_specfile;
261#[cfg(test)]
262mod rpm_specfile_test;
263mod rpm_yumdb;
264mod ruby;
265#[cfg(test)]
266mod ruby_scan_test;
267#[cfg(test)]
268mod ruby_test;
269mod sbt;
270#[cfg(test)]
271mod sbt_test;
272#[cfg(test)]
273mod scan_test_utils;
274mod swift_manifest_json;
275#[cfg(test)]
276mod swift_manifest_json_test;
277mod swift_resolved;
278#[cfg(test)]
279mod swift_resolved_test;
280#[cfg(test)]
281mod swift_scan_test;
282mod swift_show_dependencies;
283#[cfg(test)]
284mod swift_show_dependencies_test;
285pub mod utils;
286mod uv_lock;
287#[cfg(test)]
288mod uv_lock_test;
289mod vcpkg;
290#[cfg(test)]
291mod vcpkg_scan_test;
292#[cfg(test)]
293mod vcpkg_test;
294pub(crate) mod windows_executable;
295#[cfg(test)]
296mod windows_executable_golden_test;
297mod yarn_lock;
298#[cfg(test)]
299mod yarn_lock_test;
300mod yarn_pnp;
301#[cfg(test)]
302mod yarn_pnp_test;
303
304#[cfg(all(test, feature = "golden-tests"))]
305mod golden_test;
306
307use std::cell::RefCell;
308use std::panic::{AssertUnwindSafe, catch_unwind};
309use std::path::Path;
310use std::sync::Arc;
311
312use crate::license_detection::LicenseDetectionEngine;
313use crate::models::{PackageData, PackageType};
314use crate::parsers::license_normalization::finalize_package_declared_license_references;
315use crate::parsers::utils::MAX_ITERATION_COUNT;
316
317thread_local! {
318 static PARSER_DIAGNOSTIC_STACK: RefCell<Vec<Vec<String>>> = const { RefCell::new(Vec::new()) };
319 static PARSER_LICENSE_ENGINE_STACK: RefCell<Vec<Option<Arc<LicenseDetectionEngine>>>> = const { RefCell::new(Vec::new()) };
320}
321
322#[derive(Debug, Default)]
323pub struct ParsePackagesResult {
324 pub packages: Vec<PackageData>,
325 pub scan_errors: Vec<String>,
326}
327
328fn panic_payload_to_string(payload: &(dyn std::any::Any + Send)) -> String {
329 if let Some(message) = payload.downcast_ref::<&str>() {
330 (*message).to_string()
331 } else if let Some(message) = payload.downcast_ref::<String>() {
332 message.clone()
333 } else {
334 "unknown panic payload".to_string()
335 }
336}
337
338pub(crate) fn capture_parser_diagnostics<F>(
339 extract: F,
340 handler_name: &str,
341 path: &Path,
342 license_engine: Option<Arc<LicenseDetectionEngine>>,
343) -> ParsePackagesResult
344where
345 F: FnOnce() -> Vec<PackageData>,
346{
347 PARSER_DIAGNOSTIC_STACK.with(|stack| {
348 stack.borrow_mut().push(Vec::new());
349 });
350 PARSER_LICENSE_ENGINE_STACK.with(|stack| {
351 stack.borrow_mut().push(license_engine);
352 });
353
354 let extract_result = catch_unwind(AssertUnwindSafe(|| {
355 extract()
356 .into_iter()
357 .map(|mut package| {
358 finalize_package_declared_license_references(&mut package);
359 package
360 })
361 .take(MAX_ITERATION_COUNT)
362 .collect::<Vec<_>>()
363 }));
364 PARSER_LICENSE_ENGINE_STACK.with(|stack| {
365 stack.borrow_mut().pop();
366 });
367 let mut scan_errors =
368 PARSER_DIAGNOSTIC_STACK.with(|stack| stack.borrow_mut().pop().unwrap_or_default());
369
370 match extract_result {
371 Ok(packages) => ParsePackagesResult {
372 packages,
373 scan_errors,
374 },
375 Err(payload) => {
376 scan_errors.push(format!(
377 "{} panicked while parsing {}: {}",
378 handler_name,
379 path.display(),
380 panic_payload_to_string(payload.as_ref())
381 ));
382 ParsePackagesResult {
383 packages: Vec::new(),
384 scan_errors,
385 }
386 }
387 }
388}
389
390pub(crate) fn active_parser_license_engine() -> Option<Arc<LicenseDetectionEngine>> {
391 PARSER_LICENSE_ENGINE_STACK.with(|stack| stack.borrow().last().cloned().flatten())
392}
393
394pub(crate) fn record_parser_diagnostic(message: String) -> bool {
395 PARSER_DIAGNOSTIC_STACK.with(|stack| {
396 let mut stack = stack.borrow_mut();
397 let Some(active) = stack.last_mut() else {
398 return false;
399 };
400 active.push(message);
401 true
402 })
403}
404
405#[macro_export]
406macro_rules! parser_warn {
407 ($($arg:tt)*) => {{
408 let message = format!($($arg)*);
409 if !$crate::parsers::record_parser_diagnostic(message.clone()) {
410 log::warn!("{message}");
411 }
412 }};
413}
414
415pub trait PackageParser {
459 const PACKAGE_TYPE: PackageType;
461
462 fn extract_packages(path: &Path) -> Vec<PackageData>;
472
473 fn is_match(path: &Path) -> bool;
478
479 fn extract_first_package(path: &Path) -> PackageData {
482 Self::extract_packages(path)
483 .into_iter()
484 .map(|mut package| {
485 finalize_package_declared_license_references(&mut package);
486 package
487 })
488 .next()
489 .unwrap_or_default()
490 }
491}
492
493pub use self::about::AboutFileParser;
494pub use self::alpine::{AlpineApkParser, AlpineApkbuildParser, AlpineInstalledParser};
495pub use self::arch::{ArchPkginfoParser, ArchSrcinfoParser};
496pub use self::autotools::AutotoolsConfigureParser;
497pub use self::bazel::{BazelBuildParser, BazelModuleParser};
498pub use self::bower::BowerJsonParser;
499pub use self::buck::{BuckBuildParser, BuckMetadataBzlParser};
500pub use self::bun_lock::BunLockParser;
501pub use self::bun_lockb::BunLockbParser;
502pub use self::cargo::CargoParser;
503#[cfg_attr(not(test), allow(unused_imports))]
504pub use self::cargo_lock::CargoLockParser;
505pub use self::chef::{ChefMetadataJsonParser, ChefMetadataRbParser};
506pub use self::citation::CitationCffParser;
507pub use self::clojure::{ClojureDepsEdnParser, ClojureProjectCljParser};
508pub use self::composer::{ComposerJsonParser, ComposerLockParser};
509pub use self::conan::{ConanFilePyParser, ConanLockParser, ConanfileTxtParser};
510pub use self::conan_data::ConanDataParser;
511pub use self::conda::{CondaEnvironmentYmlParser, CondaMetaYamlParser};
512pub use self::conda_meta_json::CondaMetaJsonParser;
513pub use self::cpan::{CpanManifestParser, CpanMetaJsonParser, CpanMetaYmlParser};
514pub use self::cpan_dist_ini::CpanDistIniParser;
515pub use self::cpan_makefile_pl::CpanMakefilePlParser;
516pub use self::cran::CranParser;
517pub use self::dart::{PubspecLockParser, PubspecYamlParser};
518pub use self::debian::{
519 DebianControlInExtractedDebParser, DebianControlParser, DebianCopyrightParser, DebianDebParser,
520 DebianDebianTarParser, DebianDistrolessInstalledParser, DebianDscParser,
521 DebianInstalledListParser, DebianInstalledMd5sumsParser, DebianInstalledParser,
522 DebianMd5sumInPackageParser, DebianOrigTarParser,
523};
524pub use self::deno::DenoParser;
525pub use self::deno_lock::DenoLockParser;
526pub use self::docker::DockerfileParser;
527pub use self::freebsd::FreebsdCompactManifestParser;
528pub use self::gitmodules::GitmodulesParser;
529pub use self::go::{GoModParser, GoSumParser, GoWorkParser, GodepsParser};
530pub use self::go_mod_graph::GoModGraphParser;
531pub use self::gradle::GradleParser;
532pub use self::gradle_lock::GradleLockfileParser;
533pub use self::gradle_module::GradleModuleParser;
534pub use self::hackage::{HackageCabalParser, HackageCabalProjectParser, HackageStackYamlParser};
535pub use self::haxe::HaxeParser;
536pub use self::helm::{HelmChartLockParser, HelmChartYamlParser};
537pub use self::hex_lock::HexLockParser;
538pub use self::julia::{JuliaManifestTomlParser, JuliaProjectTomlParser};
539pub use self::maven::MavenParser;
540pub use self::meson::MesonParser;
541pub use self::microsoft_update_manifest::MicrosoftUpdateManifestParser;
542pub use self::misc::{
543 AndroidApkRecognizer, AndroidLibraryRecognizer, AppleDmgRecognizer, Axis2MarRecognizer,
544 Axis2ModuleXmlRecognizer, CabArchiveRecognizer, ChromeCrxRecognizer, InstallShieldRecognizer,
545 IosIpaRecognizer, IsoImageRecognizer, IvyXmlRecognizer, JBossSarRecognizer,
546 JBossServiceXmlRecognizer, JavaEarAppXmlRecognizer, JavaEarRecognizer, JavaJarRecognizer,
547 JavaWarRecognizer, JavaWarWebXmlRecognizer, MeteorPackageRecognizer, MozillaXpiRecognizer,
548 NsisRecognizer, SharArchiveRecognizer, SquashfsRecognizer,
549};
550pub use self::nix::{NixDefaultParser, NixFlakeLockParser, NixFlakeParser};
551pub use self::npm::NpmParser;
552pub use self::npm_lock::NpmLockParser;
553pub use self::npm_workspace::NpmWorkspaceParser;
554pub use self::nuget::{
555 CentralPackageManagementPropsParser, DirectoryBuildPropsParser, DotNetDepsJsonParser,
556 NupkgParser, NuspecParser, PackageReferenceProjectParser, PackagesConfigParser,
557 PackagesLockParser, ProjectJsonParser, ProjectLockJsonParser,
558};
559pub use self::opam::OpamParser;
560pub use self::os_release::OsReleaseParser;
561pub use self::pip_inspect_deplock::PipInspectDeplockParser;
562pub use self::pipfile_lock::PipfileLockParser;
563pub use self::pixi::{PixiLockParser, PixiTomlParser};
564pub use self::pnpm_lock::PnpmLockParser;
565pub use self::podfile::PodfileParser;
566pub use self::podfile_lock::PodfileLockParser;
567pub use self::podspec::PodspecParser;
568pub use self::podspec_json::PodspecJsonParser;
569pub use self::poetry_lock::PoetryLockParser;
570pub use self::publiccode::PubliccodeParser;
571pub use self::pylock_toml::PylockTomlParser;
572pub use self::python::PythonParser;
573pub use self::readme::ReadmeParser;
574pub use self::requirements_txt::RequirementsTxtParser;
575#[cfg(feature = "rpm-sqlite")]
576pub use self::rpm_db::RpmSqliteDatabaseParser;
577pub use self::rpm_db::{RpmBdbDatabaseParser, RpmNdbDatabaseParser};
578pub use self::rpm_license_files::RpmLicenseFilesParser;
579pub use self::rpm_mariner_manifest::RpmMarinerManifestParser;
580pub use self::rpm_parser::RpmParser;
581pub use self::rpm_specfile::RpmSpecfileParser;
582pub use self::rpm_yumdb::RpmYumdbParser;
583pub use self::ruby::{
584 GemArchiveParser, GemMetadataExtractedParser, GemfileLockParser, GemfileParser, GemspecParser,
585};
586pub use self::sbt::SbtParser;
587pub use self::swift_manifest_json::SwiftManifestJsonParser;
588pub use self::swift_resolved::SwiftPackageResolvedParser;
589pub use self::swift_show_dependencies::SwiftShowDependenciesParser;
590pub use self::uv_lock::UvLockParser;
591pub use self::vcpkg::VcpkgManifestParser;
592pub use self::yarn_lock::YarnLockParser;
593pub use self::yarn_pnp::YarnPnpParser;
594
595macro_rules! register_package_handlers {
601 (
602 parsers: [$($(#[$parser_meta:meta])* $parser:ty),* $(,)?],
603 recognizers: [$($recognizer:ty),* $(,)?] $(,)?
604 ) => {
605 pub fn try_parse_file_with_license_engine(
606 path: &Path,
607 license_engine: Option<Arc<LicenseDetectionEngine>>,
608 ) -> Option<ParsePackagesResult> {
609 $(
610 $(#[$parser_meta])*
611 if <$parser>::is_match(path) {
612 return Some(capture_parser_diagnostics(
613 || <$parser>::extract_packages(path),
614 stringify!($parser),
615 path,
616 license_engine.clone(),
617 ));
618 }
619 )*
620 $(
621 if <$recognizer>::is_match(path) {
622 return Some(capture_parser_diagnostics(
623 || <$recognizer>::extract_packages(path),
624 stringify!($recognizer),
625 path,
626 license_engine.clone(),
627 ));
628 }
629 )*
630 None
631 }
632
633 pub fn try_parse_file(path: &Path) -> Option<ParsePackagesResult> {
634 try_parse_file_with_license_engine(path, None)
635 }
636
637 #[allow(dead_code)]
640 pub fn parse_by_type_name(type_name: &str, path: &Path) -> Option<PackageData> {
641 match type_name {
642 $(
643 $(#[$parser_meta])*
644 stringify!($parser) => Some(<$parser>::extract_first_package(path)),
645 )*
646 $(
647 stringify!($recognizer) => Some(<$recognizer>::extract_first_package(path)),
648 )*
649 _ => None
650 }
651 }
652
653 #[allow(dead_code)]
656 pub fn list_parser_types() -> Vec<&'static str> {
657 vec![
658 $(
659 $(#[$parser_meta])*
660 stringify!($parser),
661 )*
662 $(
663 stringify!($recognizer),
664 )*
665 ]
666 }
667 };
668}
669
670#[cfg(test)]
671mod tests {
672 use std::collections::HashMap;
673
674 use super::{active_parser_license_engine, capture_parser_diagnostics};
675 use crate::license_detection::LicenseDetectionEngine;
676 use crate::models::PackageData;
677 use crate::parsers::license_normalization::{
678 clear_last_parser_license_engine_ptr, last_parser_license_engine_ptr,
679 };
680 use std::path::Path;
681 use std::sync::Arc;
682
683 #[test]
684 fn test_capture_parser_diagnostics_exposes_active_license_engine() {
685 let engine =
686 Arc::new(LicenseDetectionEngine::from_embedded().expect("embedded engine should load"));
687
688 let result = capture_parser_diagnostics(
689 || {
690 assert!(active_parser_license_engine().is_some());
691 vec![PackageData::default()]
692 },
693 "TestParser",
694 Path::new("testdata/package.json"),
695 Some(engine),
696 );
697
698 assert_eq!(result.packages.len(), 1);
699 assert!(active_parser_license_engine().is_none());
700 }
701
702 #[test]
703 fn test_capture_parser_diagnostics_keeps_active_license_engine_for_finalization() {
704 let engine =
705 Arc::new(LicenseDetectionEngine::from_embedded().expect("embedded engine should load"));
706 clear_last_parser_license_engine_ptr();
707
708 let result = capture_parser_diagnostics(
709 || {
710 vec![PackageData {
711 declared_license_expression: Some("mit".to_string()),
712 declared_license_expression_spdx: Some("MIT".to_string()),
713 extracted_license_statement: Some("MIT".to_string()),
714 extra_data: Some(HashMap::from([(
715 "license_file".to_string(),
716 serde_json::Value::String("LICENSE".to_string()),
717 )])),
718 ..Default::default()
719 }]
720 },
721 "TestParser",
722 Path::new("testdata/package.json"),
723 Some(Arc::clone(&engine)),
724 );
725
726 assert_eq!(result.packages.len(), 1);
727 assert_eq!(
728 last_parser_license_engine_ptr(),
729 Some(Arc::as_ptr(&engine) as usize)
730 );
731 assert_eq!(
732 result.packages[0].license_detections[0].matches[0]
733 .referenced_filenames
734 .as_ref(),
735 Some(&vec!["LICENSE".to_string()])
736 );
737 assert!(active_parser_license_engine().is_none());
738 }
739}
740
741register_package_handlers! {
742 parsers: [
743 AboutFileParser,
744 AlpineApkParser,
745 AlpineApkbuildParser,
746 AlpineInstalledParser,
747 ArchPkginfoParser,
748 ArchSrcinfoParser,
749 AutotoolsConfigureParser,
750 BazelBuildParser,
751 BazelModuleParser,
752 BowerJsonParser,
753 BunLockParser,
754 BunLockbParser,
755 BuckBuildParser,
756 BuckMetadataBzlParser,
757 CargoLockParser,
758 CargoParser,
759 ChefMetadataJsonParser,
760 ChefMetadataRbParser,
761 CitationCffParser,
762 ClojureDepsEdnParser,
763 ClojureProjectCljParser,
764 ComposerJsonParser,
765 ComposerLockParser,
766 ConanDataParser,
767 ConanFilePyParser,
768 ConanfileTxtParser,
769 ConanLockParser,
770 CondaEnvironmentYmlParser,
771 CondaMetaJsonParser,
772 CondaMetaYamlParser,
773 CpanDistIniParser,
774 CpanMakefilePlParser,
775 CpanManifestParser,
776 CpanMetaJsonParser,
777 CpanMetaYmlParser,
778 CranParser,
779 DebianControlInExtractedDebParser,
780 DebianControlParser,
781 DebianCopyrightParser,
782 DebianDebianTarParser,
783 DebianDebParser,
784 DebianDistrolessInstalledParser,
785 DebianDscParser,
786 DebianInstalledListParser,
787 DebianInstalledMd5sumsParser,
788 DebianInstalledParser,
789 DebianMd5sumInPackageParser,
790 DebianOrigTarParser,
791 DenoParser,
792 DenoLockParser,
793 DockerfileParser,
794 FreebsdCompactManifestParser,
795 GemArchiveParser,
796 GemfileLockParser,
797 GemfileParser,
798 GemMetadataExtractedParser,
799 GemspecParser,
800 GitmodulesParser,
801 GodepsParser,
802 GoModParser,
803 GoModGraphParser,
804 GoSumParser,
805 GoWorkParser,
806 GradleLockfileParser,
807 GradleParser,
808 GradleModuleParser,
809 HackageCabalParser,
810 HackageCabalProjectParser,
811 HackageStackYamlParser,
812 HelmChartYamlParser,
813 HelmChartLockParser,
814 HaxeParser,
815 HexLockParser,
816 JuliaManifestTomlParser,
817 JuliaProjectTomlParser,
818 MavenParser,
819 MesonParser,
820 MicrosoftUpdateManifestParser,
821 NixDefaultParser,
822 NixFlakeLockParser,
823 NixFlakeParser,
824 NpmLockParser,
825 NpmParser,
826 NpmWorkspaceParser,
827 DotNetDepsJsonParser,
828 CentralPackageManagementPropsParser,
829 DirectoryBuildPropsParser,
830 NupkgParser,
831 NuspecParser,
832 PackageReferenceProjectParser,
833 OpamParser,
834 OsReleaseParser,
835 PackagesConfigParser,
836 PackagesLockParser,
837 ProjectJsonParser,
838 ProjectLockJsonParser,
839 PipfileLockParser,
840 PipInspectDeplockParser,
841 PixiTomlParser,
842 PixiLockParser,
843 PnpmLockParser,
844 PodfileLockParser,
845 PodfileParser,
846 PodspecJsonParser,
847 PodspecParser,
848 PoetryLockParser,
849 PubliccodeParser,
850 PylockTomlParser,
851 PubspecLockParser,
852 PubspecYamlParser,
853 PythonParser,
854 UvLockParser,
855 VcpkgManifestParser,
856 ReadmeParser,
857 RequirementsTxtParser,
858 RpmBdbDatabaseParser,
859 RpmLicenseFilesParser,
860 RpmMarinerManifestParser,
861 RpmNdbDatabaseParser,
862 RpmParser,
863 RpmSpecfileParser,
864 #[cfg(feature = "rpm-sqlite")]
865 RpmSqliteDatabaseParser,
866 RpmYumdbParser,
867 SbtParser,
868 SwiftManifestJsonParser,
869 SwiftPackageResolvedParser,
870 SwiftShowDependenciesParser,
871 YarnLockParser,
872 YarnPnpParser,
873 ],
874 recognizers: [
875 AndroidApkRecognizer,
876 AndroidLibraryRecognizer,
877 AppleDmgRecognizer,
878 Axis2MarRecognizer,
879 Axis2ModuleXmlRecognizer,
880 CabArchiveRecognizer,
881 ChromeCrxRecognizer,
882 InstallShieldRecognizer,
883 IosIpaRecognizer,
884 IsoImageRecognizer,
885 IvyXmlRecognizer,
886 JavaEarAppXmlRecognizer,
887 JavaEarRecognizer,
888 JavaJarRecognizer,
889 JavaWarRecognizer,
890 JavaWarWebXmlRecognizer,
891 JBossSarRecognizer,
892 JBossServiceXmlRecognizer,
893 MeteorPackageRecognizer,
894 MozillaXpiRecognizer,
895 NsisRecognizer,
896 SharArchiveRecognizer,
897 SquashfsRecognizer,
898 ],
899}
900
901#[cfg(test)]
902mod panic_isolation_tests {
903 use super::*;
904
905 #[test]
906 fn capture_parser_diagnostics_turns_panics_into_scan_errors() {
907 let path = Path::new("fixtures/panic-package.json");
908 let result = capture_parser_diagnostics(
909 || -> Vec<PackageData> { panic!("panic boom") },
910 "PanicParser",
911 path,
912 None,
913 );
914
915 assert!(result.packages.is_empty());
916 assert_eq!(result.scan_errors.len(), 1);
917 assert!(result.scan_errors[0].contains("PanicParser"));
918 assert!(result.scan_errors[0].contains("fixtures/panic-package.json"));
919 assert!(result.scan_errors[0].contains("panic boom"));
920 }
921
922 #[test]
923 fn capture_parser_diagnostics_recovers_after_panic() {
924 let panic_path = Path::new("fixtures/panic-package.json");
925 let _ = capture_parser_diagnostics(
926 || -> Vec<PackageData> { panic!("panic boom") },
927 "PanicParser",
928 panic_path,
929 None,
930 );
931
932 let ok_path = Path::new("fixtures/recovered-package.json");
933 let result = capture_parser_diagnostics(
934 || {
935 crate::parser_warn!("recoverable parser warning");
936 vec![PackageData {
937 package_type: Some(PackageType::Npm),
938 ..Default::default()
939 }]
940 },
941 "RecoveringParser",
942 ok_path,
943 None,
944 );
945
946 assert_eq!(result.packages.len(), 1);
947 assert_eq!(result.scan_errors, vec!["recoverable parser warning"]);
948 }
949}