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    PubliccodeYaml,
356
357    // ── vcpkg ──
358    VcpkgJson,
359    VcpkgConfigurationJson,
360    VcpkgLockJson,
361    VcpkgControl,
362
363    // ── Yarn ──
364    YarnLock,
365    YarnLockV1,
366    YarnLockV2,
367    YarnPnpCjs,
368
369    // ── Git ──
370    Gitmodules,
371}
372
373impl DatasourceId {
374    /// Returns the string representation of this datasource ID.
375    ///
376    /// This matches the serialized form used in JSON output.
377    pub fn as_str(&self) -> &'static str {
378        match self {
379            // About/README/OS
380            Self::AboutFile => "about_file",
381            Self::Readme => "readme",
382            Self::EtcOsRelease => "etc_os_release",
383
384            // Alpine
385            Self::AlpineApkArchive => "alpine_apk_archive",
386            Self::AlpineApkbuild => "alpine_apkbuild",
387            Self::AlpineInstalledDb => "alpine_installed_db",
388
389            // Arch Linux
390            Self::ArchAurinfo => "arch_aurinfo",
391            Self::ArchPkginfo => "arch_pkginfo",
392            Self::ArchSrcinfo => "arch_srcinfo",
393
394            // Android
395            Self::AndroidAab => "android_aab",
396            Self::AndroidAarLibrary => "android_aar_library",
397            Self::AndroidApk => "android_apk",
398            Self::AndroidManifestXml => "android_manifest_xml",
399            Self::AndroidSoongMetadata => "android_soong_metadata",
400
401            // Apache Axis2
402            Self::Axis2Mar => "axis2_mar",
403            Self::Axis2ModuleXml => "axis2_module_xml",
404
405            // Autotools
406            Self::AutotoolsConfigure => "autotools_configure",
407
408            // Bazel
409            Self::BazelBuild => "bazel_build",
410
411            // Bitbake
412            Self::BitbakeRecipe => "bitbake_recipe",
413            Self::BitbakeRecipeAppend => "bitbake_recipe_append",
414
415            // Bower
416            Self::BowerJson => "bower_json",
417
418            // Buck
419            Self::BuckFile => "buck_file",
420            Self::BuckMetadata => "buck_metadata",
421
422            // Carthage
423            Self::CarthageCartfile => "carthage_cartfile",
424            Self::CarthageCartfileResolved => "carthage_cartfile_resolved",
425
426            // Cargo/Rust
427            Self::CargoLock => "cargo_lock",
428            Self::CargoToml => "cargo_toml",
429            Self::RustBinary => "rust_binary",
430            Self::WindowsExecutable => "windows_executable",
431
432            // Chef
433            Self::ChefCookbookMetadataJson => "chef_cookbook_metadata_json",
434            Self::ChefCookbookMetadataRb => "chef_cookbook_metadata_rb",
435
436            Self::CitationCff => "citation_cff",
437
438            // CocoaPods
439            Self::CocoapodsPodfile => "cocoapods_podfile",
440            Self::CocoapodsPodfileLock => "cocoapods_podfile_lock",
441            Self::CocoapodsPodspec => "cocoapods_podspec",
442            Self::CocoapodsPodspecJson => "cocoapods_podspec_json",
443
444            // Conan
445            Self::ConanConanDataYml => "conan_conandata_yml",
446            Self::ConanConanFilePy => "conan_conanfile_py",
447            Self::ConanConanFileTxt => "conan_conanfile_txt",
448            Self::ConanLock => "conan_lock",
449
450            // Conda
451            Self::CondaYaml => "conda_yaml",
452            Self::CondaMetaJson => "conda_meta_json",
453            Self::CondaMetaYaml => "conda_meta_yaml",
454
455            // Clojure
456            Self::ClojureDepsEdn => "clojure_deps_edn",
457            Self::ClojureProjectClj => "clojure_project_clj",
458
459            // CPAN/Perl
460            Self::CpanDistIni => "cpan_dist_ini",
461            Self::CpanMakefile => "cpan_makefile",
462            Self::CpanManifest => "cpan_manifest",
463            Self::CpanMetaJson => "cpan_meta_json",
464            Self::CpanMetaYml => "cpan_meta_yml",
465
466            // CRAN/R
467            Self::CranDescription => "cran_description",
468
469            // Dart/Flutter
470            Self::PubspecLock => "pubspec_lock",
471            Self::PubspecYaml => "pubspec_yaml",
472
473            // Debian
474            Self::DebianControlExtractedDeb => "debian_control_extracted_deb",
475            Self::DebianControlInSource => "debian_control_in_source",
476            Self::DebianCopyright => "debian_copyright",
477            Self::DebianCopyrightInSource => "debian_copyright_in_source",
478            Self::DebianCopyrightInPackage => "debian_copyright_in_package",
479            Self::DebianCopyrightStandalone => "debian_copyright_standalone",
480            Self::DebianDeb => "debian_deb",
481            Self::DebianSourceMetadataTarball => "debian_source_metadata_tarball",
482            Self::DebianDistrolessInstalledDb => "debian_distroless_installed_db",
483            Self::DebianInstalledFilesList => "debian_installed_files_list",
484            Self::DebianInstalledMd5Sums => "debian_installed_md5sums",
485            Self::DebianInstalledStatusDb => "debian_installed_status_db",
486            Self::DebianMd5SumsInExtractedDeb => "debian_md5sums_in_extracted_deb",
487            Self::DebianOriginalSourceTarball => "debian_original_source_tarball",
488            Self::DebianSourceControlDsc => "debian_source_control_dsc",
489            Self::DenoJson => "deno_json",
490            Self::DenoLock => "deno_lock",
491            Self::Dockerfile => "dockerfile",
492            Self::OciImageIndex => "oci_image_index",
493            Self::OciImageManifest => "oci_image_manifest",
494            Self::ErlangOtpAppSrc => "erlang_otp_app_src",
495            Self::RebarConfig => "rebar_config",
496            Self::RebarLock => "rebar_lock",
497            Self::BazelModule => "bazel_module",
498
499            // FreeBSD
500            Self::FreebsdCompactManifest => "freebsd_compact_manifest",
501
502            // Go
503            Self::Godeps => "godeps",
504            Self::GoBinary => "go_binary",
505            Self::GoMod => "go_mod",
506            Self::GoModGraph => "go_mod_graph",
507            Self::GoSum => "go_sum",
508            Self::GoWork => "go_work",
509
510            // Haskell / Hackage
511            Self::HackageCabal => "hackage_cabal",
512            Self::HackageCabalProject => "hackage_cabal_project",
513            Self::HackageStackYaml => "hackage_stack_yaml",
514
515            // Gradle
516            Self::BuildGradle => "build_gradle",
517            Self::GradleLockfile => "gradle_lockfile",
518            Self::GradleModule => "gradle_module",
519
520            // Haxe
521            Self::HaxelibJson => "haxelib_json",
522
523            // Helm
524            Self::HelmChartLock => "helm_chart_lock",
525            Self::HelmChartYaml => "helm_chart_yaml",
526
527            // Hex/Elixir
528            Self::HexMixExs => "hex_mix_exs",
529            Self::HexMixLock => "hex_mix_lock",
530
531            // Hugging Face
532            Self::HuggingfaceModelCard => "huggingface_model_card",
533            Self::HuggingfaceConfigJson => "huggingface_config_json",
534            Self::HuggingfaceModelIndexJson => "huggingface_model_index_json",
535
536            // Julia
537            Self::JuliaProjectToml => "julia_project_toml",
538            Self::JuliaManifestToml => "julia_manifest_toml",
539
540            // Java
541            Self::AntIvyDependenciesProperties => "ant_ivy_dependencies_properties",
542            Self::AntIvyXml => "ant_ivy_xml",
543            Self::JavaEarApplicationXml => "java_ear_application_xml",
544            Self::JavaEarArchive => "java_ear_archive",
545            Self::JavaJar => "java_jar",
546            Self::JavaJarManifest => "java_jar_manifest",
547            Self::JavaOsgiManifest => "java_osgi_manifest",
548            Self::JavaWarArchive => "java_war_archive",
549            Self::JavaWarWebXml => "java_war_web_xml",
550            Self::JbossSar => "jboss_sar",
551            Self::JbossServiceXml => "jboss_service_xml",
552
553            // Maven
554            Self::MavenPom => "maven_pom",
555            Self::MavenPomProperties => "maven_pom_properties",
556            Self::MesonBuild => "meson_build",
557            Self::SbtBuildSbt => "sbt_build_sbt",
558
559            // Microsoft
560            Self::MicrosoftCabinet => "microsoft_cabinet",
561            Self::MicrosoftUpdateManifestMum => "microsoft_update_manifest_mum",
562
563            // Mobile/Browser
564            Self::AppleDmg => "apple_dmg",
565            Self::ChromeCrx => "chrome_crx",
566            Self::IosIpa => "ios_ipa",
567            Self::MozillaXpi => "mozilla_xpi",
568
569            // Meteor
570            Self::MeteorPackage => "meteor_package",
571
572            Self::NixDefaultNix => "nix_default_nix",
573            Self::NixFlakeLock => "nix_flake_lock",
574            Self::NixFlakeNix => "nix_flake_nix",
575
576            // npm
577            Self::BunLock => "bun_lock",
578            Self::BunLockb => "bun_lockb",
579            Self::NpmPackageJson => "npm_package_json",
580            Self::NpmPackageLockJson => "npm_package_lock_json",
581
582            // NuGet
583            Self::NugetCsproj => "nuget_csproj",
584            Self::NugetDepsJson => "nuget_deps_json",
585            Self::NugetDirectoryBuildProps => "nuget_directory_build_props",
586            Self::NugetDirectoryPackagesProps => "nuget_directory_packages_props",
587            Self::NugetNupkg => "nuget_nupkg",
588            Self::NugetProjectJson => "nuget_project_json",
589            Self::NugetProjectLockJson => "nuget_project_lock_json",
590            Self::NugetPackagesConfig => "nuget_packages_config",
591            Self::NugetPackagesLock => "nuget_packages_lock",
592            Self::NugetNuspec => "nuget_nuspec",
593            Self::NugetVbproj => "nuget_vbproj",
594            Self::NugetFsproj => "nuget_fsproj",
595
596            // OCaml/opam
597            Self::OpamFile => "opam_file",
598
599            // PHP/Composer
600            Self::PhpComposerJson => "php_composer_json",
601            Self::PhpComposerLock => "php_composer_lock",
602
603            // pnpm
604            Self::PnpmLockYaml => "pnpm_lock_yaml",
605            Self::PnpmWorkspaceYaml => "pnpm_workspace_yaml",
606
607            // Python/PyPI
608            Self::Pipfile => "pipfile",
609            Self::PipfileLock => "pipfile_lock",
610            Self::PipRequirements => "pip_requirements",
611            Self::PixiLock => "pixi_lock",
612            Self::PixiToml => "pixi_toml",
613            Self::PypiPipOriginJson => "pypi_pip_origin_json",
614            Self::PypiEgg => "pypi_egg",
615            Self::PypiEggPkginfo => "pypi_egg_pkginfo",
616            Self::PypiEditableEggPkginfo => "pypi_editable_egg_pkginfo",
617            Self::PypiInspectDeplock => "pypi_inspect_deplock",
618            Self::PypiJson => "pypi_json",
619            Self::PypiPoetryLock => "pypi_poetry_lock",
620            Self::PypiPoetryPyprojectToml => "pypi_poetry_pyproject_toml",
621            Self::PypiSdist => "pypi_sdist",
622            Self::PypiPylockToml => "pypi_pylock_toml",
623            Self::PypiPyprojectToml => "pypi_pyproject_toml",
624            Self::PypiSdistPkginfo => "pypi_sdist_pkginfo",
625            Self::PypiSetupCfg => "pypi_setup_cfg",
626            Self::PypiSetupPy => "pypi_setup_py",
627            Self::PypiUvLock => "pypi_uv_lock",
628            Self::PypiWheel => "pypi_wheel",
629            Self::PypiWheelMetadata => "pypi_wheel_metadata",
630
631            // RPM
632            Self::RpmArchive => "rpm_archive",
633            Self::RpmInstalledDatabaseBdb => "rpm_installed_database_bdb",
634            Self::RpmInstalledDatabaseNdb => "rpm_installed_database_ndb",
635            Self::RpmInstalledDatabaseSqlite => "rpm_installed_database_sqlite",
636            Self::RpmMarinerManifest => "rpm_mariner_manifest",
637            Self::RpmPackageLicenses => "rpm_package_licenses",
638            Self::RpmSpecfile => "rpm_specfile",
639            Self::RpmYumdb => "rpm_yumdb",
640
641            // Ruby/RubyGems
642            Self::Gemfile => "gemfile",
643            Self::GemfileExtracted => "gemfile_extracted",
644            Self::GemfileLock => "gemfile_lock",
645            Self::GemfileLockExtracted => "gemfile_lock_extracted",
646            Self::GemArchive => "gem_archive",
647            Self::GemArchiveExtracted => "gem_archive_extracted",
648            Self::Gemspec => "gemspec",
649            Self::GemspecExtracted => "gemspec_extracted",
650            Self::GemGemspecInstalledSpecifications => "gem_gemspec_installed_specifications",
651
652            // Disk Images/Installers
653            Self::InstallshieldInstaller => "installshield_installer",
654            Self::IsoDiskImage => "iso_disk_image",
655            Self::NsisInstaller => "nsis_installer",
656            Self::SharShellArchive => "shar_shell_archive",
657            Self::SquashfsDiskImage => "squashfs_disk_image",
658
659            // Swift
660            Self::SwiftPackageManifestJson => "swift_package_manifest_json",
661            Self::SwiftPackageResolved => "swift_package_resolved",
662            Self::SwiftPackageShowDependencies => "swift_package_show_dependencies",
663
664            Self::PubliccodeYaml => "publiccode_yaml",
665
666            // vcpkg
667            Self::VcpkgJson => "vcpkg_json",
668            Self::VcpkgConfigurationJson => "vcpkg_configuration_json",
669            Self::VcpkgLockJson => "vcpkg_lock_json",
670            Self::VcpkgControl => "vcpkg_control",
671
672            // Yarn
673            Self::YarnLock => "yarn_lock",
674            Self::YarnLockV1 => "yarn_lock_v1",
675            Self::YarnLockV2 => "yarn_lock_v2",
676            Self::YarnPnpCjs => "yarn_pnp_cjs",
677
678            // Git
679            Self::Gitmodules => "gitmodules",
680        }
681    }
682}
683
684impl AsRef<str> for DatasourceId {
685    fn as_ref(&self) -> &str {
686        self.as_str()
687    }
688}
689
690impl fmt::Display for DatasourceId {
691    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
692        f.write_str(self.as_str())
693    }
694}
695
696impl FromStr for DatasourceId {
697    type Err = String;
698
699    fn from_str(s: &str) -> Result<Self, Self::Err> {
700        use strum::IntoEnumIterator;
701        Self::iter()
702            .find(|variant| variant.as_str() == s)
703            .ok_or_else(|| format!("unknown datasource id: {}", s))
704    }
705}
706
707#[cfg(test)]
708mod tests {
709    use super::*;
710
711    #[test]
712    fn test_serialization() {
713        let id = DatasourceId::NpmPackageJson;
714        let json = serde_json::to_string(&id).unwrap();
715        assert_eq!(json, r#""NpmPackageJson""#);
716    }
717
718    #[test]
719    fn test_deserialization() {
720        let json = r#""NpmPackageJson""#;
721        let id: DatasourceId = serde_json::from_str(json).unwrap();
722        assert_eq!(id, DatasourceId::NpmPackageJson);
723    }
724
725    #[test]
726    fn test_as_str() {
727        assert_eq!(DatasourceId::NpmPackageJson.as_str(), "npm_package_json");
728        assert_eq!(DatasourceId::CargoLock.as_str(), "cargo_lock");
729        assert_eq!(
730            DatasourceId::PypiPyprojectToml.as_str(),
731            "pypi_pyproject_toml"
732        );
733        assert_eq!(DatasourceId::HackageCabal.as_str(), "hackage_cabal");
734        assert_eq!(DatasourceId::CitationCff.as_str(), "citation_cff");
735        assert_eq!(DatasourceId::PubliccodeYaml.as_str(), "publiccode_yaml");
736        assert_eq!(DatasourceId::YarnPnpCjs.as_str(), "yarn_pnp_cjs");
737    }
738
739    #[test]
740    fn test_display() {
741        assert_eq!(DatasourceId::NpmPackageJson.to_string(), "npm_package_json");
742    }
743
744    #[test]
745    fn test_as_ref() {
746        let id = DatasourceId::NpmPackageJson;
747        let s: &str = id.as_ref();
748        assert_eq!(s, "npm_package_json");
749    }
750
751    #[test]
752    fn test_python_rename_mappings() {
753        // Test the ~12 IDs that changed from our old values to match Python
754        assert_eq!(DatasourceId::BuckFile.as_str(), "buck_file");
755        assert_eq!(DatasourceId::BuckMetadata.as_str(), "buck_metadata");
756        assert_eq!(
757            DatasourceId::ChefCookbookMetadataJson.as_str(),
758            "chef_cookbook_metadata_json"
759        );
760        assert_eq!(
761            DatasourceId::ChefCookbookMetadataRb.as_str(),
762            "chef_cookbook_metadata_rb"
763        );
764        assert_eq!(DatasourceId::CondaYaml.as_str(), "conda_yaml");
765        assert_eq!(DatasourceId::CpanMakefile.as_str(), "cpan_makefile");
766        assert_eq!(
767            DatasourceId::DebianInstalledFilesList.as_str(),
768            "debian_installed_files_list"
769        );
770        assert_eq!(
771            DatasourceId::DebianOriginalSourceTarball.as_str(),
772            "debian_original_source_tarball"
773        );
774        assert_eq!(
775            DatasourceId::DebianSourceMetadataTarball.as_str(),
776            "debian_source_metadata_tarball"
777        );
778        assert_eq!(
779            DatasourceId::GemArchiveExtracted.as_str(),
780            "gem_archive_extracted"
781        );
782        // Corrected from typos in Python reference
783        assert_eq!(DatasourceId::NugetNuspec.as_str(), "nuget_nuspec");
784        assert_eq!(DatasourceId::RpmSpecfile.as_str(), "rpm_specfile");
785    }
786
787    #[test]
788    fn test_canonical_serialization() {
789        let nuget_json = serde_json::to_string(&DatasourceId::NugetNuspec).unwrap();
790        assert_eq!(nuget_json, r#""NugetNuspec""#);
791
792        let rpm_json = serde_json::to_string(&DatasourceId::RpmSpecfile).unwrap();
793        assert_eq!(rpm_json, r#""RpmSpecfile""#);
794    }
795
796    #[test]
797    fn test_from_str_uses_canonical_ids_and_rejects_legacy_typos() {
798        assert_eq!(
799            DatasourceId::from_str("nuget_nuspec").unwrap(),
800            DatasourceId::NugetNuspec
801        );
802        assert_eq!(
803            DatasourceId::from_str("rpm_specfile").unwrap(),
804            DatasourceId::RpmSpecfile
805        );
806        // ScanCode's upstream `nuget_nupsec` / `rpm_spefile` typos are
807        // intentionally not accepted: Provenant emits and parses only the
808        // corrected ids, so the legacy aliases were dead no-ops.
809        assert!(DatasourceId::from_str("nuget_nupsec").is_err());
810        assert!(DatasourceId::from_str("rpm_spefile").is_err());
811    }
812}