Skip to main content

provenant/models/
datasource_id.rs

1// SPDX-FileCopyrightText: Provenant contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Datasource identifiers for package parsers.
5//!
6//! Each variant uniquely identifies the type of package data source (file format)
7//! that was parsed. These IDs enable the assembly system to intelligently merge
8//! related package files.
9
10use serde::{Deserialize, Serialize};
11use std::fmt;
12use strum::{EnumCount, EnumIter};
13
14/// Unique identifier for the type of package data source (file format).
15///
16/// Datasource IDs distinguish between different file types within the same ecosystem
17/// (e.g., `NpmPackageJson` vs `NpmPackageLockJson`). The assembly system uses these
18/// IDs to match packages from related files for merging into a single logical package.
19///
20/// # Serialization
21///
22/// Variants serialize to snake_case strings matching the Python reference values.
23/// The JSON output is identical to the Python ScanCode Toolkit.
24///
25/// # Examples
26///
27/// ```ignore
28/// use provenant::models::DatasourceId;
29///
30/// let id = DatasourceId::NpmPackageJson;
31/// assert_eq!(id.as_ref(), "npm_package_json");
32/// assert_eq!(id.to_string(), "npm_package_json");
33/// ```
34#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, EnumCount, EnumIter)]
35#[serde(rename_all = "snake_case")]
36pub enum DatasourceId {
37    // ── About/README/OS ──
38    AboutFile,
39    Readme,
40    EtcOsRelease,
41
42    // ── Alpine ──
43    AlpineApkArchive,
44    AlpineApkbuild,
45    AlpineInstalledDb,
46
47    // ── Arch Linux ──
48    ArchAurinfo,
49    ArchPkginfo,
50    ArchSrcinfo,
51
52    // ── Android ──
53    AndroidAab,
54    AndroidAarLibrary,
55    AndroidApk,
56    AndroidManifestXml,
57    AndroidSoongMetadata,
58
59    // ── Apache Axis2 ──
60    Axis2Mar,
61    Axis2ModuleXml,
62
63    // ── Autotools ──
64    AutotoolsConfigure,
65
66    // ── Bazel ──
67    BazelBuild,
68    BazelModule,
69
70    // ── Bitbake ──
71    BitbakeRecipe,
72    BitbakeRecipeAppend,
73
74    // ── Bower ──
75    BowerJson,
76
77    // ── Buck ──
78    /// Matches Python reference value. More consistent name would be `buck_file`.
79    #[serde(rename = "buck_file")]
80    BuckFile,
81    /// Matches Python reference value. More consistent name would be `buck_metadata`.
82    #[serde(rename = "buck_metadata")]
83    BuckMetadata,
84
85    // ── Bun ──
86    BunLock,
87    BunLockb,
88
89    // ── Carthage ──
90    CarthageCartfile,
91    CarthageCartfileResolved,
92
93    // ── Cargo/Rust ──
94    CargoLock,
95    CargoToml,
96    RustBinary,
97    WindowsExecutable,
98
99    // ── Chef ──
100    /// Matches Python reference value.
101    #[serde(rename = "chef_cookbook_metadata_json")]
102    ChefCookbookMetadataJson,
103    /// Matches Python reference value.
104    #[serde(rename = "chef_cookbook_metadata_rb")]
105    ChefCookbookMetadataRb,
106
107    CitationCff,
108
109    // ── CocoaPods ──
110    CocoapodsPodfile,
111    CocoapodsPodfileLock,
112    CocoapodsPodspec,
113    CocoapodsPodspecJson,
114
115    // ── Conan ──
116    #[serde(rename = "conan_conandata_yml")]
117    ConanConanDataYml,
118    #[serde(rename = "conan_conanfile_py")]
119    ConanConanFilePy,
120    #[serde(rename = "conan_conanfile_txt")]
121    ConanConanFileTxt,
122    ConanLock,
123
124    // ── Conda ──
125    /// Matches Python reference value.
126    #[serde(rename = "conda_yaml")]
127    CondaYaml,
128    CondaMetaJson,
129    CondaMetaYaml,
130
131    // ── Clojure ──
132    ClojureDepsEdn,
133    ClojureProjectClj,
134
135    // ── CPAN/Perl ──
136    CpanDistIni,
137    /// Matches Python reference value.
138    #[serde(rename = "cpan_makefile")]
139    CpanMakefile,
140    CpanManifest,
141    CpanMetaJson,
142    CpanMetaYml,
143
144    // ── CRAN/R ──
145    CranDescription,
146
147    // ── Dart/Flutter ──
148    PubspecLock,
149    PubspecYaml,
150
151    // ── Debian ──
152    DebianControlExtractedDeb,
153    DebianControlInSource,
154    DebianCopyright,
155    DebianCopyrightInSource,
156    DebianCopyrightInPackage,
157    DebianCopyrightStandalone,
158    DebianDeb,
159    /// Matches Python reference value.
160    #[serde(rename = "debian_source_metadata_tarball")]
161    DebianSourceMetadataTarball,
162    DebianDistrolessInstalledDb,
163    /// Matches Python reference value.
164    #[serde(rename = "debian_installed_files_list")]
165    DebianInstalledFilesList,
166    #[serde(rename = "debian_installed_md5sums")]
167    DebianInstalledMd5Sums,
168    DebianInstalledStatusDb,
169    #[serde(rename = "debian_md5sums_in_extracted_deb")]
170    DebianMd5SumsInExtractedDeb,
171    /// Matches Python reference value.
172    #[serde(rename = "debian_original_source_tarball")]
173    DebianOriginalSourceTarball,
174    DebianSourceControlDsc,
175
176    // ── Deno ──
177    DenoJson,
178    DenoLock,
179
180    // ── Docker ──
181    Dockerfile,
182
183    // ── Erlang / OTP ──
184    ErlangOtpAppSrc,
185    RebarConfig,
186    RebarLock,
187
188    // ── FreeBSD ──
189    FreebsdCompactManifest,
190
191    // ── Go ──
192    Godeps,
193    GoBinary,
194    GoMod,
195    GoModGraph,
196    GoSum,
197    GoWork,
198
199    // ── Haskell / Hackage ──
200    HackageCabal,
201    HackageCabalProject,
202    HackageStackYaml,
203
204    // ── Gradle ──
205    BuildGradle,
206    GradleLockfile,
207    GradleModule,
208
209    // ── Haxe ──
210    HaxelibJson,
211
212    // ── Helm ──
213    HelmChartLock,
214    HelmChartYaml,
215
216    // ── Hex/Elixir ──
217    HexMixLock,
218
219    // ── Julia ──
220    JuliaProjectToml,
221    JuliaManifestToml,
222
223    // ── Java ──
224    AntIvyXml,
225    JavaEarApplicationXml,
226    JavaEarArchive,
227    JavaJar,
228    JavaJarManifest,
229    JavaOsgiManifest,
230    JavaWarArchive,
231    JavaWarWebXml,
232    JbossSar,
233    JbossServiceXml,
234
235    // ── Maven ──
236    MavenPom,
237    MavenPomProperties,
238    MesonBuild,
239
240    SbtBuildSbt,
241
242    // ── Microsoft ──
243    MicrosoftCabinet,
244    MicrosoftUpdateManifestMum,
245
246    // ── Mobile/Browser ──
247    AppleDmg,
248    ChromeCrx,
249    IosIpa,
250    MozillaXpi,
251
252    // ── Meteor ──
253    MeteorPackage,
254
255    NixDefaultNix,
256    NixFlakeLock,
257    NixFlakeNix,
258
259    // ── npm ──
260    NpmPackageJson,
261    NpmPackageLockJson,
262
263    // ── NuGet ──
264    NugetCsproj,
265    NugetDepsJson,
266    NugetDirectoryBuildProps,
267    NugetDirectoryPackagesProps,
268    NugetNupkg,
269    NugetProjectJson,
270    NugetProjectLockJson,
271    NugetPackagesConfig,
272    NugetPackagesLock,
273    /// Serializes to `"nuget_nuspec"` (corrected from typo `"nuget_nupsec"` in Python reference).
274    /// Legacy alias `"nuget_nupsec"` is supported for backward compatibility with `--from-json`.
275    #[serde(rename = "nuget_nuspec", alias = "nuget_nupsec")]
276    NugetNuspec,
277    NugetVbproj,
278    NugetFsproj,
279
280    // ── OCaml/opam ──
281    OpamFile,
282
283    // ── PHP/Composer ──
284    PhpComposerJson,
285    PhpComposerLock,
286
287    // ── pnpm ──
288    PnpmLockYaml,
289    PnpmWorkspaceYaml,
290
291    // ── Python/PyPI ──
292    Pipfile,
293    PipfileLock,
294    PipRequirements,
295    PixiLock,
296    PixiToml,
297    PypiPipOriginJson,
298    PypiEgg,
299    PypiEggPkginfo,
300    PypiEditableEggPkginfo,
301    PypiInspectDeplock,
302    PypiJson,
303    PypiPoetryLock,
304    PypiPoetryPyprojectToml,
305    PypiSdist,
306    PypiPylockToml,
307    PypiPyprojectToml,
308    PypiSdistPkginfo,
309    PypiSetupCfg,
310    PypiSetupPy,
311    PypiUvLock,
312    PypiWheel,
313    PypiWheelMetadata,
314
315    // ── RPM ──
316    RpmArchive,
317    RpmInstalledDatabaseBdb,
318    RpmInstalledDatabaseNdb,
319    RpmInstalledDatabaseSqlite,
320    RpmMarinerManifest,
321    RpmPackageLicenses,
322    /// Serializes to `"rpm_specfile"` (corrected from typo `"rpm_spefile"` in Python reference).
323    /// Legacy alias `"rpm_spefile"` is supported for backward compatibility with `--from-json`.
324    #[serde(rename = "rpm_specfile", alias = "rpm_spefile")]
325    RpmSpecfile,
326    RpmYumdb,
327
328    // ── Ruby/RubyGems ──
329    Gemfile,
330    GemfileExtracted,
331    GemfileLock,
332    GemfileLockExtracted,
333    GemArchive,
334    /// Matches Python reference value.
335    #[serde(rename = "gem_archive_extracted")]
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::ErlangOtpAppSrc => "erlang_otp_app_src",
488            Self::RebarConfig => "rebar_config",
489            Self::RebarLock => "rebar_lock",
490            Self::BazelModule => "bazel_module",
491
492            // FreeBSD
493            Self::FreebsdCompactManifest => "freebsd_compact_manifest",
494
495            // Go
496            Self::Godeps => "godeps",
497            Self::GoBinary => "go_binary",
498            Self::GoMod => "go_mod",
499            Self::GoModGraph => "go_mod_graph",
500            Self::GoSum => "go_sum",
501            Self::GoWork => "go_work",
502
503            // Haskell / Hackage
504            Self::HackageCabal => "hackage_cabal",
505            Self::HackageCabalProject => "hackage_cabal_project",
506            Self::HackageStackYaml => "hackage_stack_yaml",
507
508            // Gradle
509            Self::BuildGradle => "build_gradle",
510            Self::GradleLockfile => "gradle_lockfile",
511            Self::GradleModule => "gradle_module",
512
513            // Haxe
514            Self::HaxelibJson => "haxelib_json",
515
516            // Helm
517            Self::HelmChartLock => "helm_chart_lock",
518            Self::HelmChartYaml => "helm_chart_yaml",
519
520            // Hex/Elixir
521            Self::HexMixLock => "hex_mix_lock",
522
523            // Julia
524            Self::JuliaProjectToml => "julia_project_toml",
525            Self::JuliaManifestToml => "julia_manifest_toml",
526
527            // Java
528            Self::AntIvyXml => "ant_ivy_xml",
529            Self::JavaEarApplicationXml => "java_ear_application_xml",
530            Self::JavaEarArchive => "java_ear_archive",
531            Self::JavaJar => "java_jar",
532            Self::JavaJarManifest => "java_jar_manifest",
533            Self::JavaOsgiManifest => "java_osgi_manifest",
534            Self::JavaWarArchive => "java_war_archive",
535            Self::JavaWarWebXml => "java_war_web_xml",
536            Self::JbossSar => "jboss_sar",
537            Self::JbossServiceXml => "jboss_service_xml",
538
539            // Maven
540            Self::MavenPom => "maven_pom",
541            Self::MavenPomProperties => "maven_pom_properties",
542            Self::MesonBuild => "meson_build",
543            Self::SbtBuildSbt => "sbt_build_sbt",
544
545            // Microsoft
546            Self::MicrosoftCabinet => "microsoft_cabinet",
547            Self::MicrosoftUpdateManifestMum => "microsoft_update_manifest_mum",
548
549            // Mobile/Browser
550            Self::AppleDmg => "apple_dmg",
551            Self::ChromeCrx => "chrome_crx",
552            Self::IosIpa => "ios_ipa",
553            Self::MozillaXpi => "mozilla_xpi",
554
555            // Meteor
556            Self::MeteorPackage => "meteor_package",
557
558            Self::NixDefaultNix => "nix_default_nix",
559            Self::NixFlakeLock => "nix_flake_lock",
560            Self::NixFlakeNix => "nix_flake_nix",
561
562            // npm
563            Self::BunLock => "bun_lock",
564            Self::BunLockb => "bun_lockb",
565            Self::NpmPackageJson => "npm_package_json",
566            Self::NpmPackageLockJson => "npm_package_lock_json",
567
568            // NuGet
569            Self::NugetCsproj => "nuget_csproj",
570            Self::NugetDepsJson => "nuget_deps_json",
571            Self::NugetDirectoryBuildProps => "nuget_directory_build_props",
572            Self::NugetDirectoryPackagesProps => "nuget_directory_packages_props",
573            Self::NugetNupkg => "nuget_nupkg",
574            Self::NugetProjectJson => "nuget_project_json",
575            Self::NugetProjectLockJson => "nuget_project_lock_json",
576            Self::NugetPackagesConfig => "nuget_packages_config",
577            Self::NugetPackagesLock => "nuget_packages_lock",
578            Self::NugetNuspec => "nuget_nuspec",
579            Self::NugetVbproj => "nuget_vbproj",
580            Self::NugetFsproj => "nuget_fsproj",
581
582            // OCaml/opam
583            Self::OpamFile => "opam_file",
584
585            // PHP/Composer
586            Self::PhpComposerJson => "php_composer_json",
587            Self::PhpComposerLock => "php_composer_lock",
588
589            // pnpm
590            Self::PnpmLockYaml => "pnpm_lock_yaml",
591            Self::PnpmWorkspaceYaml => "pnpm_workspace_yaml",
592
593            // Python/PyPI
594            Self::Pipfile => "pipfile",
595            Self::PipfileLock => "pipfile_lock",
596            Self::PipRequirements => "pip_requirements",
597            Self::PixiLock => "pixi_lock",
598            Self::PixiToml => "pixi_toml",
599            Self::PypiPipOriginJson => "pypi_pip_origin_json",
600            Self::PypiEgg => "pypi_egg",
601            Self::PypiEggPkginfo => "pypi_egg_pkginfo",
602            Self::PypiEditableEggPkginfo => "pypi_editable_egg_pkginfo",
603            Self::PypiInspectDeplock => "pypi_inspect_deplock",
604            Self::PypiJson => "pypi_json",
605            Self::PypiPoetryLock => "pypi_poetry_lock",
606            Self::PypiPoetryPyprojectToml => "pypi_poetry_pyproject_toml",
607            Self::PypiSdist => "pypi_sdist",
608            Self::PypiPylockToml => "pypi_pylock_toml",
609            Self::PypiPyprojectToml => "pypi_pyproject_toml",
610            Self::PypiSdistPkginfo => "pypi_sdist_pkginfo",
611            Self::PypiSetupCfg => "pypi_setup_cfg",
612            Self::PypiSetupPy => "pypi_setup_py",
613            Self::PypiUvLock => "pypi_uv_lock",
614            Self::PypiWheel => "pypi_wheel",
615            Self::PypiWheelMetadata => "pypi_wheel_metadata",
616
617            // RPM
618            Self::RpmArchive => "rpm_archive",
619            Self::RpmInstalledDatabaseBdb => "rpm_installed_database_bdb",
620            Self::RpmInstalledDatabaseNdb => "rpm_installed_database_ndb",
621            Self::RpmInstalledDatabaseSqlite => "rpm_installed_database_sqlite",
622            Self::RpmMarinerManifest => "rpm_mariner_manifest",
623            Self::RpmPackageLicenses => "rpm_package_licenses",
624            Self::RpmSpecfile => "rpm_specfile",
625            Self::RpmYumdb => "rpm_yumdb",
626
627            // Ruby/RubyGems
628            Self::Gemfile => "gemfile",
629            Self::GemfileExtracted => "gemfile_extracted",
630            Self::GemfileLock => "gemfile_lock",
631            Self::GemfileLockExtracted => "gemfile_lock_extracted",
632            Self::GemArchive => "gem_archive",
633            Self::GemArchiveExtracted => "gem_archive_extracted",
634            Self::Gemspec => "gemspec",
635            Self::GemspecExtracted => "gemspec_extracted",
636            Self::GemGemspecInstalledSpecifications => "gem_gemspec_installed_specifications",
637
638            // Disk Images/Installers
639            Self::InstallshieldInstaller => "installshield_installer",
640            Self::IsoDiskImage => "iso_disk_image",
641            Self::NsisInstaller => "nsis_installer",
642            Self::SharShellArchive => "shar_shell_archive",
643            Self::SquashfsDiskImage => "squashfs_disk_image",
644
645            // Swift
646            Self::SwiftPackageManifestJson => "swift_package_manifest_json",
647            Self::SwiftPackageResolved => "swift_package_resolved",
648            Self::SwiftPackageShowDependencies => "swift_package_show_dependencies",
649
650            Self::PubliccodeYaml => "publiccode_yaml",
651
652            // vcpkg
653            Self::VcpkgJson => "vcpkg_json",
654
655            // Yarn
656            Self::YarnLock => "yarn_lock",
657            Self::YarnLockV1 => "yarn_lock_v1",
658            Self::YarnLockV2 => "yarn_lock_v2",
659            Self::YarnPnpCjs => "yarn_pnp_cjs",
660
661            // Git
662            Self::Gitmodules => "gitmodules",
663        }
664    }
665}
666
667impl AsRef<str> for DatasourceId {
668    fn as_ref(&self) -> &str {
669        self.as_str()
670    }
671}
672
673impl fmt::Display for DatasourceId {
674    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
675        f.write_str(self.as_str())
676    }
677}
678
679#[cfg(test)]
680mod tests {
681    use super::*;
682
683    #[test]
684    fn test_serialization() {
685        let id = DatasourceId::NpmPackageJson;
686        let json = serde_json::to_string(&id).unwrap();
687        assert_eq!(json, r#""npm_package_json""#);
688    }
689
690    #[test]
691    fn test_deserialization() {
692        let json = r#""npm_package_json""#;
693        let id: DatasourceId = serde_json::from_str(json).unwrap();
694        assert_eq!(id, DatasourceId::NpmPackageJson);
695    }
696
697    #[test]
698    fn test_as_str() {
699        assert_eq!(DatasourceId::NpmPackageJson.as_str(), "npm_package_json");
700        assert_eq!(DatasourceId::CargoLock.as_str(), "cargo_lock");
701        assert_eq!(
702            DatasourceId::PypiPyprojectToml.as_str(),
703            "pypi_pyproject_toml"
704        );
705        assert_eq!(DatasourceId::HackageCabal.as_str(), "hackage_cabal");
706        assert_eq!(DatasourceId::CitationCff.as_str(), "citation_cff");
707        assert_eq!(DatasourceId::PubliccodeYaml.as_str(), "publiccode_yaml");
708        assert_eq!(DatasourceId::YarnPnpCjs.as_str(), "yarn_pnp_cjs");
709    }
710
711    #[test]
712    fn test_display() {
713        assert_eq!(DatasourceId::NpmPackageJson.to_string(), "npm_package_json");
714    }
715
716    #[test]
717    fn test_as_ref() {
718        let id = DatasourceId::NpmPackageJson;
719        let s: &str = id.as_ref();
720        assert_eq!(s, "npm_package_json");
721    }
722
723    #[test]
724    fn test_python_rename_mappings() {
725        // Test the ~12 IDs that changed from our old values to match Python
726        assert_eq!(DatasourceId::BuckFile.as_str(), "buck_file");
727        assert_eq!(DatasourceId::BuckMetadata.as_str(), "buck_metadata");
728        assert_eq!(
729            DatasourceId::ChefCookbookMetadataJson.as_str(),
730            "chef_cookbook_metadata_json"
731        );
732        assert_eq!(
733            DatasourceId::ChefCookbookMetadataRb.as_str(),
734            "chef_cookbook_metadata_rb"
735        );
736        assert_eq!(DatasourceId::CondaYaml.as_str(), "conda_yaml");
737        assert_eq!(DatasourceId::CpanMakefile.as_str(), "cpan_makefile");
738        assert_eq!(
739            DatasourceId::DebianInstalledFilesList.as_str(),
740            "debian_installed_files_list"
741        );
742        assert_eq!(
743            DatasourceId::DebianOriginalSourceTarball.as_str(),
744            "debian_original_source_tarball"
745        );
746        assert_eq!(
747            DatasourceId::DebianSourceMetadataTarball.as_str(),
748            "debian_source_metadata_tarball"
749        );
750        assert_eq!(
751            DatasourceId::GemArchiveExtracted.as_str(),
752            "gem_archive_extracted"
753        );
754        // Corrected from typos in Python reference
755        assert_eq!(DatasourceId::NugetNuspec.as_str(), "nuget_nuspec");
756        assert_eq!(DatasourceId::RpmSpecfile.as_str(), "rpm_specfile");
757    }
758
759    #[test]
760    fn test_legacy_deserialization_aliases() {
761        // Test that legacy typo spellings are accepted for backward compatibility
762        let legacy_nuget: DatasourceId = serde_json::from_str(r#""nuget_nupsec""#).unwrap();
763        assert_eq!(legacy_nuget, DatasourceId::NugetNuspec);
764
765        let legacy_rpm: DatasourceId = serde_json::from_str(r#""rpm_spefile""#).unwrap();
766        assert_eq!(legacy_rpm, DatasourceId::RpmSpecfile);
767
768        // Canonical spellings should also work
769        let canonical_nuget: DatasourceId = serde_json::from_str(r#""nuget_nuspec""#).unwrap();
770        assert_eq!(canonical_nuget, DatasourceId::NugetNuspec);
771
772        let canonical_rpm: DatasourceId = serde_json::from_str(r#""rpm_specfile""#).unwrap();
773        assert_eq!(canonical_rpm, DatasourceId::RpmSpecfile);
774    }
775
776    #[test]
777    fn test_canonical_serialization() {
778        // Test that canonical spellings are used for serialization
779        let nuget_json = serde_json::to_string(&DatasourceId::NugetNuspec).unwrap();
780        assert_eq!(nuget_json, r#""nuget_nuspec""#);
781
782        let rpm_json = serde_json::to_string(&DatasourceId::RpmSpecfile).unwrap();
783        assert_eq!(rpm_json, r#""rpm_specfile""#);
784    }
785}