Skip to main content

provenant/models/
datasource_id.rs

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