Skip to main content

provenant/models/
datasource_id.rs

1// SPDX-FileCopyrightText: nexB Inc. and others
2// ScanCode is a trademark of nexB Inc.
3// SPDX-FileCopyrightText: Provenant contributors
4// SPDX-License-Identifier: Apache-2.0
5// Derived from ScanCode Toolkit (Apache-2.0); modified. See NOTICE.
6
7//! Datasource identifiers for package parsers.
8//!
9//! Each variant uniquely identifies the type of package data source (file format)
10//! that was parsed. These IDs enable the assembly system to intelligently merge
11//! related package files.
12
13use serde::{Deserialize, Serialize};
14use std::fmt;
15use std::str::FromStr;
16use strum::{EnumCount, EnumIter};
17
18/// Unique identifier for the type of package data source (file format).
19///
20/// Datasource IDs distinguish between different file types within the same ecosystem
21/// (e.g., `NpmPackageJson` vs `NpmPackageLockJson`). The assembly system uses these
22/// IDs to match packages from related files for merging into a single logical package.
23///
24/// # Serialization
25///
26/// Variants serialize as PascalCase in the cache/spill format (e.g., `NpmPackageJson`).
27/// For JSON output, use `as_str()` / `Display` which returns snake_case strings
28/// matching the Python ScanCode Toolkit values (e.g., `npm_package_json`).
29///
30/// # Ordering
31///
32/// `PartialOrd`/`Ord` are derived and therefore follow **declaration order**. The assembly
33/// stage iterates active assembler configs via a `BTreeSet<DatasourceId>`, so adding or
34/// reordering variants changes the per-directory assembler execution sequence in polyglot
35/// directories.
36#[derive(
37    Clone,
38    Copy,
39    Debug,
40    PartialEq,
41    Eq,
42    PartialOrd,
43    Ord,
44    Hash,
45    Serialize,
46    Deserialize,
47    EnumCount,
48    EnumIter,
49)]
50pub enum DatasourceId {
51    // ── About/README/OS ──
52    AboutFile,
53    Readme,
54    EtcOsRelease,
55
56    // ── Alpine ──
57    AlpineApkArchive,
58    AlpineApkbuild,
59    AlpineInstalledDb,
60
61    // ── Arch Linux ──
62    ArchAurinfo,
63    ArchPkginfo,
64    ArchSrcinfo,
65
66    // ── Android ──
67    AndroidAab,
68    AndroidAarLibrary,
69    AndroidApk,
70    AndroidManifestXml,
71    AndroidSoongMetadata,
72
73    // ── Apache Axis2 ──
74    Axis2Mar,
75    Axis2ModuleXml,
76
77    // ── Autotools ──
78    AutotoolsConfigure,
79
80    // ── Bazel ──
81    BazelBuild,
82    BazelModule,
83
84    // ── Bitbake ──
85    BitbakeRecipe,
86    BitbakeRecipeAppend,
87
88    // ── Bower ──
89    BowerJson,
90
91    // ── Buck ──
92    BuckFile,
93    BuckMetadata,
94
95    // ── Bun ──
96    BunLock,
97    BunLockb,
98
99    // ── Carthage ──
100    CarthageCartfile,
101    CarthageCartfileResolved,
102
103    // ── Cargo/Rust ──
104    CargoLock,
105    CargoToml,
106    RustBinary,
107    WindowsExecutable,
108
109    // ── Chef ──
110    /// Matches Python reference value.
111    ChefCookbookMetadataJson,
112    /// Matches Python reference value.
113    ChefCookbookMetadataRb,
114
115    CitationCff,
116
117    // ── CocoaPods ──
118    CocoapodsPodfile,
119    CocoapodsPodfileLock,
120    CocoapodsPodspec,
121    CocoapodsPodspecJson,
122
123    // ── Conan ──
124    ConanConanDataYml,
125    ConanConanFilePy,
126    ConanConanFileTxt,
127    ConanLock,
128
129    // ── Conda ──
130    /// Matches Python reference value.
131    CondaYaml,
132    CondaMetaJson,
133    CondaMetaYaml,
134
135    // ── Clojure ──
136    ClojureDepsEdn,
137    ClojureProjectClj,
138
139    // ── CPAN/Perl ──
140    CpanDistIni,
141    /// Matches Python reference value.
142    CpanMakefile,
143    CpanManifest,
144    CpanMetaJson,
145    CpanMetaYml,
146
147    // ── CRAN/R ──
148    CranDescription,
149
150    // ── Dart/Flutter ──
151    PubspecLock,
152    PubspecYaml,
153
154    // ── Debian ──
155    DebianControlExtractedDeb,
156    DebianControlInSource,
157    DebianCopyright,
158    DebianCopyrightInSource,
159    DebianCopyrightInPackage,
160    DebianCopyrightStandalone,
161    DebianDeb,
162    /// Matches Python reference value.
163    DebianSourceMetadataTarball,
164    DebianDistrolessInstalledDb,
165    /// Matches Python reference value.
166    DebianInstalledFilesList,
167    DebianInstalledMd5Sums,
168    DebianInstalledStatusDb,
169    DebianMd5SumsInExtractedDeb,
170    /// Matches Python reference value.
171    DebianOriginalSourceTarball,
172    DebianSourceControlDsc,
173
174    // ── Deno ──
175    DenoJson,
176    DenoLock,
177
178    // ── Docker ──
179    Dockerfile,
180
181    // ── OCI image ──
182    OciImageIndex,
183    OciImageManifest,
184
185    // ── Erlang / OTP ──
186    ErlangOtpAppSrc,
187    RebarConfig,
188    RebarLock,
189
190    // ── FreeBSD ──
191    FreebsdCompactManifest,
192
193    // ── Go ──
194    Godeps,
195    GoBinary,
196    GoMod,
197    GoModGraph,
198    GoSum,
199    GoWork,
200
201    // ── Haskell / Hackage ──
202    HackageCabal,
203    HackageCabalProject,
204    HackageStackYaml,
205
206    // ── Gradle ──
207    BuildGradle,
208    GradleLockfile,
209    GradleModule,
210
211    // ── Haxe ──
212    HaxelibJson,
213
214    // ── Helm ──
215    HelmChartLock,
216    HelmChartYaml,
217
218    // ── Hex/Elixir ──
219    HexMixExs,
220    HexMixLock,
221
222    // ── Hugging Face ──
223    HuggingfaceModelCard,
224    HuggingfaceConfigJson,
225    HuggingfaceModelIndexJson,
226
227    // ── Julia ──
228    JuliaProjectToml,
229    JuliaManifestToml,
230
231    // ── Java ──
232    AntIvyDependenciesProperties,
233    AntIvyXml,
234    JavaEarApplicationXml,
235    JavaEarArchive,
236    JavaJar,
237    JavaJarManifest,
238    JavaOsgiManifest,
239    JavaWarArchive,
240    JavaWarWebXml,
241    JbossSar,
242    JbossServiceXml,
243
244    // ── Maven ──
245    MavenPom,
246    MavenPomProperties,
247    MesonBuild,
248
249    SbtBuildSbt,
250
251    // ── Microsoft ──
252    MicrosoftCabinet,
253    MicrosoftUpdateManifestMum,
254
255    // ── Mobile/Browser ──
256    AppleDmg,
257    ChromeCrx,
258    IosIpa,
259    MozillaXpi,
260
261    // ── Meteor ──
262    MeteorPackage,
263
264    NixDefaultNix,
265    NixFlakeLock,
266    NixFlakeNix,
267
268    // ── npm ──
269    NpmPackageJson,
270    NpmPackageLockJson,
271
272    // ── NuGet ──
273    NugetCsproj,
274    NugetDepsJson,
275    NugetDirectoryBuildProps,
276    NugetDirectoryPackagesProps,
277    NugetNupkg,
278    NugetProjectJson,
279    NugetProjectLockJson,
280    NugetPackagesConfig,
281    NugetPackagesLock,
282    NugetNuspec,
283    NugetVbproj,
284    NugetFsproj,
285
286    // ── OCaml/opam ──
287    OpamFile,
288
289    // ── PHP/Composer ──
290    PhpComposerJson,
291    PhpComposerLock,
292
293    // ── pnpm ──
294    PnpmLockYaml,
295    PnpmWorkspaceYaml,
296
297    // ── Python/PyPI ──
298    Pipfile,
299    PipfileLock,
300    PipRequirements,
301    PixiLock,
302    PixiToml,
303    PypiPipOriginJson,
304    PypiEgg,
305    PypiEggPkginfo,
306    PypiEditableEggPkginfo,
307    PypiInspectDeplock,
308    PypiJson,
309    PypiPoetryLock,
310    PypiPoetryPyprojectToml,
311    PypiSdist,
312    PypiPylockToml,
313    PypiPyprojectToml,
314    PypiSdistPkginfo,
315    PypiSetupCfg,
316    PypiSetupPy,
317    PypiUvLock,
318    PypiWheel,
319    PypiWheelMetadata,
320
321    // ── RPM ──
322    RpmArchive,
323    RpmInstalledDatabaseBdb,
324    RpmInstalledDatabaseNdb,
325    RpmInstalledDatabaseSqlite,
326    RpmMarinerManifest,
327    RpmPackageLicenses,
328    RpmSpecfile,
329    RpmYumdb,
330
331    // ── Ruby/RubyGems ──
332    Gemfile,
333    GemfileExtracted,
334    GemfileLock,
335    GemfileLockExtracted,
336    GemArchive,
337    /// Matches Python reference value.
338    GemArchiveExtracted,
339    Gemspec,
340    GemspecExtracted,
341    GemGemspecInstalledSpecifications,
342
343    // ── Disk Images/Installers ──
344    InstallshieldInstaller,
345    IsoDiskImage,
346    NsisInstaller,
347    SharShellArchive,
348    SquashfsDiskImage,
349
350    // ── Swift ──
351    SwiftPackageManifestJson,
352    SwiftPackageResolved,
353    SwiftPackageShowDependencies,
354
355    // ── VS Code ──
356    VscodeExtensionVsixManifest,
357
358    PubliccodeYaml,
359
360    // ── vcpkg ──
361    VcpkgJson,
362    VcpkgConfigurationJson,
363    VcpkgLockJson,
364    VcpkgControl,
365
366    // ── Yarn ──
367    YarnLock,
368    YarnLockV1,
369    YarnLockV2,
370    YarnPnpCjs,
371
372    // ── Git ──
373    Gitmodules,
374}
375
376impl DatasourceId {
377    /// Returns the string representation of this datasource ID.
378    ///
379    /// This matches the serialized form used in JSON output.
380    pub fn as_str(&self) -> &'static str {
381        match self {
382            // About/README/OS
383            Self::AboutFile => "about_file",
384            Self::Readme => "readme",
385            Self::EtcOsRelease => "etc_os_release",
386
387            // Alpine
388            Self::AlpineApkArchive => "alpine_apk_archive",
389            Self::AlpineApkbuild => "alpine_apkbuild",
390            Self::AlpineInstalledDb => "alpine_installed_db",
391
392            // Arch Linux
393            Self::ArchAurinfo => "arch_aurinfo",
394            Self::ArchPkginfo => "arch_pkginfo",
395            Self::ArchSrcinfo => "arch_srcinfo",
396
397            // Android
398            Self::AndroidAab => "android_aab",
399            Self::AndroidAarLibrary => "android_aar_library",
400            Self::AndroidApk => "android_apk",
401            Self::AndroidManifestXml => "android_manifest_xml",
402            Self::AndroidSoongMetadata => "android_soong_metadata",
403
404            // Apache Axis2
405            Self::Axis2Mar => "axis2_mar",
406            Self::Axis2ModuleXml => "axis2_module_xml",
407
408            // Autotools
409            Self::AutotoolsConfigure => "autotools_configure",
410
411            // Bazel
412            Self::BazelBuild => "bazel_build",
413
414            // Bitbake
415            Self::BitbakeRecipe => "bitbake_recipe",
416            Self::BitbakeRecipeAppend => "bitbake_recipe_append",
417
418            // Bower
419            Self::BowerJson => "bower_json",
420
421            // Buck
422            Self::BuckFile => "buck_file",
423            Self::BuckMetadata => "buck_metadata",
424
425            // Carthage
426            Self::CarthageCartfile => "carthage_cartfile",
427            Self::CarthageCartfileResolved => "carthage_cartfile_resolved",
428
429            // Cargo/Rust
430            Self::CargoLock => "cargo_lock",
431            Self::CargoToml => "cargo_toml",
432            Self::RustBinary => "rust_binary",
433            Self::WindowsExecutable => "windows_executable",
434
435            // Chef
436            Self::ChefCookbookMetadataJson => "chef_cookbook_metadata_json",
437            Self::ChefCookbookMetadataRb => "chef_cookbook_metadata_rb",
438
439            Self::CitationCff => "citation_cff",
440
441            // CocoaPods
442            Self::CocoapodsPodfile => "cocoapods_podfile",
443            Self::CocoapodsPodfileLock => "cocoapods_podfile_lock",
444            Self::CocoapodsPodspec => "cocoapods_podspec",
445            Self::CocoapodsPodspecJson => "cocoapods_podspec_json",
446
447            // Conan
448            Self::ConanConanDataYml => "conan_conandata_yml",
449            Self::ConanConanFilePy => "conan_conanfile_py",
450            Self::ConanConanFileTxt => "conan_conanfile_txt",
451            Self::ConanLock => "conan_lock",
452
453            // Conda
454            Self::CondaYaml => "conda_yaml",
455            Self::CondaMetaJson => "conda_meta_json",
456            Self::CondaMetaYaml => "conda_meta_yaml",
457
458            // Clojure
459            Self::ClojureDepsEdn => "clojure_deps_edn",
460            Self::ClojureProjectClj => "clojure_project_clj",
461
462            // CPAN/Perl
463            Self::CpanDistIni => "cpan_dist_ini",
464            Self::CpanMakefile => "cpan_makefile",
465            Self::CpanManifest => "cpan_manifest",
466            Self::CpanMetaJson => "cpan_meta_json",
467            Self::CpanMetaYml => "cpan_meta_yml",
468
469            // CRAN/R
470            Self::CranDescription => "cran_description",
471
472            // Dart/Flutter
473            Self::PubspecLock => "pubspec_lock",
474            Self::PubspecYaml => "pubspec_yaml",
475
476            // Debian
477            Self::DebianControlExtractedDeb => "debian_control_extracted_deb",
478            Self::DebianControlInSource => "debian_control_in_source",
479            Self::DebianCopyright => "debian_copyright",
480            Self::DebianCopyrightInSource => "debian_copyright_in_source",
481            Self::DebianCopyrightInPackage => "debian_copyright_in_package",
482            Self::DebianCopyrightStandalone => "debian_copyright_standalone",
483            Self::DebianDeb => "debian_deb",
484            Self::DebianSourceMetadataTarball => "debian_source_metadata_tarball",
485            Self::DebianDistrolessInstalledDb => "debian_distroless_installed_db",
486            Self::DebianInstalledFilesList => "debian_installed_files_list",
487            Self::DebianInstalledMd5Sums => "debian_installed_md5sums",
488            Self::DebianInstalledStatusDb => "debian_installed_status_db",
489            Self::DebianMd5SumsInExtractedDeb => "debian_md5sums_in_extracted_deb",
490            Self::DebianOriginalSourceTarball => "debian_original_source_tarball",
491            Self::DebianSourceControlDsc => "debian_source_control_dsc",
492            Self::DenoJson => "deno_json",
493            Self::DenoLock => "deno_lock",
494            Self::Dockerfile => "dockerfile",
495            Self::OciImageIndex => "oci_image_index",
496            Self::OciImageManifest => "oci_image_manifest",
497            Self::ErlangOtpAppSrc => "erlang_otp_app_src",
498            Self::RebarConfig => "rebar_config",
499            Self::RebarLock => "rebar_lock",
500            Self::BazelModule => "bazel_module",
501
502            // FreeBSD
503            Self::FreebsdCompactManifest => "freebsd_compact_manifest",
504
505            // Go
506            Self::Godeps => "godeps",
507            Self::GoBinary => "go_binary",
508            Self::GoMod => "go_mod",
509            Self::GoModGraph => "go_mod_graph",
510            Self::GoSum => "go_sum",
511            Self::GoWork => "go_work",
512
513            // Haskell / Hackage
514            Self::HackageCabal => "hackage_cabal",
515            Self::HackageCabalProject => "hackage_cabal_project",
516            Self::HackageStackYaml => "hackage_stack_yaml",
517
518            // Gradle
519            Self::BuildGradle => "build_gradle",
520            Self::GradleLockfile => "gradle_lockfile",
521            Self::GradleModule => "gradle_module",
522
523            // Haxe
524            Self::HaxelibJson => "haxelib_json",
525
526            // Helm
527            Self::HelmChartLock => "helm_chart_lock",
528            Self::HelmChartYaml => "helm_chart_yaml",
529
530            // Hex/Elixir
531            Self::HexMixExs => "hex_mix_exs",
532            Self::HexMixLock => "hex_mix_lock",
533
534            // Hugging Face
535            Self::HuggingfaceModelCard => "huggingface_model_card",
536            Self::HuggingfaceConfigJson => "huggingface_config_json",
537            Self::HuggingfaceModelIndexJson => "huggingface_model_index_json",
538
539            // Julia
540            Self::JuliaProjectToml => "julia_project_toml",
541            Self::JuliaManifestToml => "julia_manifest_toml",
542
543            // Java
544            Self::AntIvyDependenciesProperties => "ant_ivy_dependencies_properties",
545            Self::AntIvyXml => "ant_ivy_xml",
546            Self::JavaEarApplicationXml => "java_ear_application_xml",
547            Self::JavaEarArchive => "java_ear_archive",
548            Self::JavaJar => "java_jar",
549            Self::JavaJarManifest => "java_jar_manifest",
550            Self::JavaOsgiManifest => "java_osgi_manifest",
551            Self::JavaWarArchive => "java_war_archive",
552            Self::JavaWarWebXml => "java_war_web_xml",
553            Self::JbossSar => "jboss_sar",
554            Self::JbossServiceXml => "jboss_service_xml",
555
556            // Maven
557            Self::MavenPom => "maven_pom",
558            Self::MavenPomProperties => "maven_pom_properties",
559            Self::MesonBuild => "meson_build",
560            Self::SbtBuildSbt => "sbt_build_sbt",
561
562            // Microsoft
563            Self::MicrosoftCabinet => "microsoft_cabinet",
564            Self::MicrosoftUpdateManifestMum => "microsoft_update_manifest_mum",
565
566            // Mobile/Browser
567            Self::AppleDmg => "apple_dmg",
568            Self::ChromeCrx => "chrome_crx",
569            Self::IosIpa => "ios_ipa",
570            Self::MozillaXpi => "mozilla_xpi",
571
572            // Meteor
573            Self::MeteorPackage => "meteor_package",
574
575            Self::NixDefaultNix => "nix_default_nix",
576            Self::NixFlakeLock => "nix_flake_lock",
577            Self::NixFlakeNix => "nix_flake_nix",
578
579            // npm
580            Self::BunLock => "bun_lock",
581            Self::BunLockb => "bun_lockb",
582            Self::NpmPackageJson => "npm_package_json",
583            Self::NpmPackageLockJson => "npm_package_lock_json",
584
585            // NuGet
586            Self::NugetCsproj => "nuget_csproj",
587            Self::NugetDepsJson => "nuget_deps_json",
588            Self::NugetDirectoryBuildProps => "nuget_directory_build_props",
589            Self::NugetDirectoryPackagesProps => "nuget_directory_packages_props",
590            Self::NugetNupkg => "nuget_nupkg",
591            Self::NugetProjectJson => "nuget_project_json",
592            Self::NugetProjectLockJson => "nuget_project_lock_json",
593            Self::NugetPackagesConfig => "nuget_packages_config",
594            Self::NugetPackagesLock => "nuget_packages_lock",
595            Self::NugetNuspec => "nuget_nuspec",
596            Self::NugetVbproj => "nuget_vbproj",
597            Self::NugetFsproj => "nuget_fsproj",
598
599            // OCaml/opam
600            Self::OpamFile => "opam_file",
601
602            // PHP/Composer
603            Self::PhpComposerJson => "php_composer_json",
604            Self::PhpComposerLock => "php_composer_lock",
605
606            // pnpm
607            Self::PnpmLockYaml => "pnpm_lock_yaml",
608            Self::PnpmWorkspaceYaml => "pnpm_workspace_yaml",
609
610            // Python/PyPI
611            Self::Pipfile => "pipfile",
612            Self::PipfileLock => "pipfile_lock",
613            Self::PipRequirements => "pip_requirements",
614            Self::PixiLock => "pixi_lock",
615            Self::PixiToml => "pixi_toml",
616            Self::PypiPipOriginJson => "pypi_pip_origin_json",
617            Self::PypiEgg => "pypi_egg",
618            Self::PypiEggPkginfo => "pypi_egg_pkginfo",
619            Self::PypiEditableEggPkginfo => "pypi_editable_egg_pkginfo",
620            Self::PypiInspectDeplock => "pypi_inspect_deplock",
621            Self::PypiJson => "pypi_json",
622            Self::PypiPoetryLock => "pypi_poetry_lock",
623            Self::PypiPoetryPyprojectToml => "pypi_poetry_pyproject_toml",
624            Self::PypiSdist => "pypi_sdist",
625            Self::PypiPylockToml => "pypi_pylock_toml",
626            Self::PypiPyprojectToml => "pypi_pyproject_toml",
627            Self::PypiSdistPkginfo => "pypi_sdist_pkginfo",
628            Self::PypiSetupCfg => "pypi_setup_cfg",
629            Self::PypiSetupPy => "pypi_setup_py",
630            Self::PypiUvLock => "pypi_uv_lock",
631            Self::PypiWheel => "pypi_wheel",
632            Self::PypiWheelMetadata => "pypi_wheel_metadata",
633
634            // RPM
635            Self::RpmArchive => "rpm_archive",
636            Self::RpmInstalledDatabaseBdb => "rpm_installed_database_bdb",
637            Self::RpmInstalledDatabaseNdb => "rpm_installed_database_ndb",
638            Self::RpmInstalledDatabaseSqlite => "rpm_installed_database_sqlite",
639            Self::RpmMarinerManifest => "rpm_mariner_manifest",
640            Self::RpmPackageLicenses => "rpm_package_licenses",
641            Self::RpmSpecfile => "rpm_specfile",
642            Self::RpmYumdb => "rpm_yumdb",
643
644            // Ruby/RubyGems
645            Self::Gemfile => "gemfile",
646            Self::GemfileExtracted => "gemfile_extracted",
647            Self::GemfileLock => "gemfile_lock",
648            Self::GemfileLockExtracted => "gemfile_lock_extracted",
649            Self::GemArchive => "gem_archive",
650            Self::GemArchiveExtracted => "gem_archive_extracted",
651            Self::Gemspec => "gemspec",
652            Self::GemspecExtracted => "gemspec_extracted",
653            Self::GemGemspecInstalledSpecifications => "gem_gemspec_installed_specifications",
654
655            // Disk Images/Installers
656            Self::InstallshieldInstaller => "installshield_installer",
657            Self::IsoDiskImage => "iso_disk_image",
658            Self::NsisInstaller => "nsis_installer",
659            Self::SharShellArchive => "shar_shell_archive",
660            Self::SquashfsDiskImage => "squashfs_disk_image",
661
662            // Swift
663            Self::SwiftPackageManifestJson => "swift_package_manifest_json",
664            Self::SwiftPackageResolved => "swift_package_resolved",
665            Self::SwiftPackageShowDependencies => "swift_package_show_dependencies",
666
667            // VS Code
668            Self::VscodeExtensionVsixManifest => "vscode_extension_vsixmanifest",
669
670            Self::PubliccodeYaml => "publiccode_yaml",
671
672            // vcpkg
673            Self::VcpkgJson => "vcpkg_json",
674            Self::VcpkgConfigurationJson => "vcpkg_configuration_json",
675            Self::VcpkgLockJson => "vcpkg_lock_json",
676            Self::VcpkgControl => "vcpkg_control",
677
678            // Yarn
679            Self::YarnLock => "yarn_lock",
680            Self::YarnLockV1 => "yarn_lock_v1",
681            Self::YarnLockV2 => "yarn_lock_v2",
682            Self::YarnPnpCjs => "yarn_pnp_cjs",
683
684            // Git
685            Self::Gitmodules => "gitmodules",
686        }
687    }
688}
689
690impl AsRef<str> for DatasourceId {
691    fn as_ref(&self) -> &str {
692        self.as_str()
693    }
694}
695
696impl fmt::Display for DatasourceId {
697    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
698        f.write_str(self.as_str())
699    }
700}
701
702impl FromStr for DatasourceId {
703    type Err = String;
704
705    fn from_str(s: &str) -> Result<Self, Self::Err> {
706        use strum::IntoEnumIterator;
707        Self::iter()
708            .find(|variant| variant.as_str() == s)
709            .ok_or_else(|| format!("unknown datasource id: {}", s))
710    }
711}
712
713#[cfg(test)]
714mod tests {
715    use super::*;
716
717    #[test]
718    fn test_serialization() {
719        let id = DatasourceId::NpmPackageJson;
720        let json = serde_json::to_string(&id).unwrap();
721        assert_eq!(json, r#""NpmPackageJson""#);
722    }
723
724    #[test]
725    fn test_deserialization() {
726        let json = r#""NpmPackageJson""#;
727        let id: DatasourceId = serde_json::from_str(json).unwrap();
728        assert_eq!(id, DatasourceId::NpmPackageJson);
729    }
730
731    #[test]
732    fn test_as_str() {
733        assert_eq!(DatasourceId::NpmPackageJson.as_str(), "npm_package_json");
734        assert_eq!(DatasourceId::CargoLock.as_str(), "cargo_lock");
735        assert_eq!(
736            DatasourceId::PypiPyprojectToml.as_str(),
737            "pypi_pyproject_toml"
738        );
739        assert_eq!(DatasourceId::HackageCabal.as_str(), "hackage_cabal");
740        assert_eq!(DatasourceId::CitationCff.as_str(), "citation_cff");
741        assert_eq!(DatasourceId::PubliccodeYaml.as_str(), "publiccode_yaml");
742        assert_eq!(DatasourceId::YarnPnpCjs.as_str(), "yarn_pnp_cjs");
743        assert_eq!(
744            DatasourceId::VscodeExtensionVsixManifest.as_str(),
745            "vscode_extension_vsixmanifest"
746        );
747    }
748
749    #[test]
750    fn test_display() {
751        assert_eq!(DatasourceId::NpmPackageJson.to_string(), "npm_package_json");
752    }
753
754    #[test]
755    fn test_as_ref() {
756        let id = DatasourceId::NpmPackageJson;
757        let s: &str = id.as_ref();
758        assert_eq!(s, "npm_package_json");
759    }
760
761    #[test]
762    fn test_python_rename_mappings() {
763        // Test the ~12 IDs that changed from our old values to match Python
764        assert_eq!(DatasourceId::BuckFile.as_str(), "buck_file");
765        assert_eq!(DatasourceId::BuckMetadata.as_str(), "buck_metadata");
766        assert_eq!(
767            DatasourceId::ChefCookbookMetadataJson.as_str(),
768            "chef_cookbook_metadata_json"
769        );
770        assert_eq!(
771            DatasourceId::ChefCookbookMetadataRb.as_str(),
772            "chef_cookbook_metadata_rb"
773        );
774        assert_eq!(DatasourceId::CondaYaml.as_str(), "conda_yaml");
775        assert_eq!(DatasourceId::CpanMakefile.as_str(), "cpan_makefile");
776        assert_eq!(
777            DatasourceId::DebianInstalledFilesList.as_str(),
778            "debian_installed_files_list"
779        );
780        assert_eq!(
781            DatasourceId::DebianOriginalSourceTarball.as_str(),
782            "debian_original_source_tarball"
783        );
784        assert_eq!(
785            DatasourceId::DebianSourceMetadataTarball.as_str(),
786            "debian_source_metadata_tarball"
787        );
788        assert_eq!(
789            DatasourceId::GemArchiveExtracted.as_str(),
790            "gem_archive_extracted"
791        );
792        // Corrected from typos in Python reference
793        assert_eq!(DatasourceId::NugetNuspec.as_str(), "nuget_nuspec");
794        assert_eq!(DatasourceId::RpmSpecfile.as_str(), "rpm_specfile");
795    }
796
797    #[test]
798    fn test_canonical_serialization() {
799        let nuget_json = serde_json::to_string(&DatasourceId::NugetNuspec).unwrap();
800        assert_eq!(nuget_json, r#""NugetNuspec""#);
801
802        let rpm_json = serde_json::to_string(&DatasourceId::RpmSpecfile).unwrap();
803        assert_eq!(rpm_json, r#""RpmSpecfile""#);
804    }
805
806    #[test]
807    fn test_from_str_uses_canonical_ids_and_rejects_legacy_typos() {
808        assert_eq!(
809            DatasourceId::from_str("nuget_nuspec").unwrap(),
810            DatasourceId::NugetNuspec
811        );
812        assert_eq!(
813            DatasourceId::from_str("rpm_specfile").unwrap(),
814            DatasourceId::RpmSpecfile
815        );
816        // ScanCode's upstream `nuget_nupsec` / `rpm_spefile` typos are
817        // intentionally not accepted: Provenant emits and parses only the
818        // corrected ids, so the legacy aliases were dead no-ops.
819        assert!(DatasourceId::from_str("nuget_nupsec").is_err());
820        assert!(DatasourceId::from_str("rpm_spefile").is_err());
821    }
822}