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