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