Skip to main content

provenant/models/
package_type.rs

1// SPDX-FileCopyrightText: Provenant contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Package type identifiers for package parsers.
5//!
6//! Each variant uniquely identifies the package ecosystem/registry type.
7//! These are used in Package URL (purl) type fields and in the JSON output
8//! as the `"type"` field of package data.
9
10use serde::{Deserialize, Serialize};
11use std::fmt;
12use std::str::FromStr;
13
14/// Package ecosystem/registry type identifier.
15///
16/// Identifies the package manager or ecosystem a package belongs to
17/// (e.g., npm, PyPI, Maven, Cargo). Used as the `"type"` field in
18/// ScanCode Toolkit-compatible JSON output.
19///
20/// This enum includes both standard purl types and ScanCode-specific types
21/// for file format recognizers (e.g., `Jar`, `War`) and metadata sources
22/// (e.g., `About`, `Readme`). For the official list of standardized purl types, see:
23/// <https://github.com/package-url/purl-spec/blob/main/purl-types-index.json>
24///
25/// # Serialization
26///
27/// Variants serialize as PascalCase in the cache/spill format (e.g., `JbossService`).
28/// For JSON output, use `as_str()` / `Display` which returns lowercase/kebab-case
29/// strings matching the Python ScanCode Toolkit values (e.g., `jboss-service`).
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
31pub enum PackageType {
32    About,
33    Alpm,
34    Alpine,
35    Android,
36    AndroidLib,
37    Autotools,
38    Axis2,
39    Bazel,
40    Bitbake,
41    Bower,
42    Buck,
43    Cab,
44    Cargo,
45    Carthage,
46    Chef,
47    Chrome,
48    Cocoapods,
49    Composer,
50    Conan,
51    Conda,
52    Cpan,
53    Cran,
54    Dart,
55    Deb,
56    Deno,
57    Docker,
58    Dmg,
59    Ear,
60    Freebsd,
61    Gem,
62    Generic,
63    Github,
64    Golang,
65    Hackage,
66    Haxe,
67    Helm,
68    Hex,
69    Installshield,
70    Julia,
71    Ios,
72    Iso,
73    Ivy,
74    Jar,
75    JbossService,
76    LinuxDistro,
77    Maven,
78    Meson,
79    Meteor,
80    Nix,
81    Mozilla,
82    Npm,
83    Nsis,
84    Nuget,
85    Opam,
86    Osgi,
87    PnpmLock,
88    Pubspec,
89    Pypi,
90    Pixi,
91    Publiccode,
92    Readme,
93    Rpm,
94    Shar,
95    Squashfs,
96    Swift,
97    Vcpkg,
98    War,
99    Winexe,
100    WindowsUpdate,
101}
102
103impl PackageType {
104    /// Returns the string representation of this package type.
105    ///
106    /// This matches the serialized form used in JSON output.
107    pub fn as_str(&self) -> &'static str {
108        match self {
109            Self::About => "about",
110            Self::Alpm => "alpm",
111            Self::Alpine => "alpine",
112            Self::Android => "android",
113            Self::AndroidLib => "android_lib",
114            Self::Autotools => "autotools",
115            Self::Axis2 => "axis2",
116            Self::Bazel => "bazel",
117            Self::Bitbake => "bitbake",
118            Self::Bower => "bower",
119            Self::Buck => "buck",
120            Self::Cab => "cab",
121            Self::Cargo => "cargo",
122            Self::Carthage => "carthage",
123            Self::Chef => "chef",
124            Self::Chrome => "chrome",
125            Self::Cocoapods => "cocoapods",
126            Self::Composer => "composer",
127            Self::Conan => "conan",
128            Self::Conda => "conda",
129            Self::Cpan => "cpan",
130            Self::Cran => "cran",
131            Self::Dart => "dart",
132            Self::Deb => "deb",
133            Self::Deno => "deno",
134            Self::Docker => "docker",
135            Self::Dmg => "dmg",
136            Self::Ear => "ear",
137            Self::Freebsd => "freebsd",
138            Self::Gem => "gem",
139            Self::Generic => "generic",
140            Self::Github => "github",
141            Self::Golang => "golang",
142            Self::Hackage => "hackage",
143            Self::Haxe => "haxe",
144            Self::Helm => "helm",
145            Self::Hex => "hex",
146            Self::Installshield => "installshield",
147            Self::Julia => "julia",
148            Self::Ios => "ios",
149            Self::Iso => "iso",
150            Self::Ivy => "ivy",
151            Self::Jar => "jar",
152            Self::JbossService => "jboss-service",
153            Self::LinuxDistro => "linux-distro",
154            Self::Maven => "maven",
155            Self::Meson => "meson",
156            Self::Meteor => "meteor",
157            Self::Nix => "nix",
158            Self::Mozilla => "mozilla",
159            Self::Npm => "npm",
160            Self::Nsis => "nsis",
161            Self::Nuget => "nuget",
162            Self::Opam => "opam",
163            Self::Osgi => "osgi",
164            Self::PnpmLock => "pnpm-lock",
165            Self::Pubspec => "pubspec",
166            Self::Pypi => "pypi",
167            Self::Pixi => "pixi",
168            Self::Publiccode => "publiccode",
169            Self::Readme => "readme",
170            Self::Rpm => "rpm",
171            Self::Shar => "shar",
172            Self::Squashfs => "squashfs",
173            Self::Swift => "swift",
174            Self::Vcpkg => "vcpkg",
175            Self::War => "war",
176            Self::Winexe => "winexe",
177            Self::WindowsUpdate => "windows-update",
178        }
179    }
180}
181
182impl AsRef<str> for PackageType {
183    fn as_ref(&self) -> &str {
184        self.as_str()
185    }
186}
187
188impl fmt::Display for PackageType {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        f.write_str(self.as_str())
191    }
192}
193
194impl FromStr for PackageType {
195    type Err = String;
196
197    fn from_str(s: &str) -> Result<Self, Self::Err> {
198        match s {
199            "about" => Ok(Self::About),
200            "alpm" => Ok(Self::Alpm),
201            "alpine" => Ok(Self::Alpine),
202            "android" => Ok(Self::Android),
203            "android_lib" => Ok(Self::AndroidLib),
204            "autotools" => Ok(Self::Autotools),
205            "axis2" => Ok(Self::Axis2),
206            "bazel" => Ok(Self::Bazel),
207            "bitbake" => Ok(Self::Bitbake),
208            "bower" => Ok(Self::Bower),
209            "buck" => Ok(Self::Buck),
210            "cab" => Ok(Self::Cab),
211            "cargo" => Ok(Self::Cargo),
212            "carthage" => Ok(Self::Carthage),
213            "chef" => Ok(Self::Chef),
214            "chrome" => Ok(Self::Chrome),
215            "cocoapods" => Ok(Self::Cocoapods),
216            "composer" => Ok(Self::Composer),
217            "conan" => Ok(Self::Conan),
218            "conda" => Ok(Self::Conda),
219            "cpan" => Ok(Self::Cpan),
220            "cran" => Ok(Self::Cran),
221            "dart" => Ok(Self::Dart),
222            "deb" => Ok(Self::Deb),
223            "deno" => Ok(Self::Deno),
224            "docker" => Ok(Self::Docker),
225            "dmg" => Ok(Self::Dmg),
226            "ear" => Ok(Self::Ear),
227            "freebsd" => Ok(Self::Freebsd),
228            "gem" => Ok(Self::Gem),
229            "generic" => Ok(Self::Generic),
230            "github" => Ok(Self::Github),
231            "golang" => Ok(Self::Golang),
232            "hackage" => Ok(Self::Hackage),
233            "haxe" => Ok(Self::Haxe),
234            "helm" => Ok(Self::Helm),
235            "hex" => Ok(Self::Hex),
236            "installshield" => Ok(Self::Installshield),
237            "julia" => Ok(Self::Julia),
238            "ios" => Ok(Self::Ios),
239            "iso" => Ok(Self::Iso),
240            "ivy" => Ok(Self::Ivy),
241            "jar" => Ok(Self::Jar),
242            "jboss-service" => Ok(Self::JbossService),
243            "linux-distro" => Ok(Self::LinuxDistro),
244            "maven" => Ok(Self::Maven),
245            "meson" => Ok(Self::Meson),
246            "meteor" => Ok(Self::Meteor),
247            "nix" => Ok(Self::Nix),
248            "mozilla" => Ok(Self::Mozilla),
249            "npm" => Ok(Self::Npm),
250            "nsis" => Ok(Self::Nsis),
251            "nuget" => Ok(Self::Nuget),
252            "opam" => Ok(Self::Opam),
253            "osgi" => Ok(Self::Osgi),
254            "pnpm-lock" => Ok(Self::PnpmLock),
255            "pubspec" => Ok(Self::Pubspec),
256            "pypi" => Ok(Self::Pypi),
257            "pixi" => Ok(Self::Pixi),
258            "publiccode" => Ok(Self::Publiccode),
259            "readme" => Ok(Self::Readme),
260            "rpm" => Ok(Self::Rpm),
261            "shar" => Ok(Self::Shar),
262            "squashfs" => Ok(Self::Squashfs),
263            "swift" => Ok(Self::Swift),
264            "vcpkg" => Ok(Self::Vcpkg),
265            "war" => Ok(Self::War),
266            "winexe" => Ok(Self::Winexe),
267            "windows-update" => Ok(Self::WindowsUpdate),
268            _ => Err(format!("unknown package type: {}", s)),
269        }
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276
277    #[test]
278    fn test_serialization() {
279        let pt = PackageType::Npm;
280        let json = serde_json::to_string(&pt).unwrap();
281        assert_eq!(json, r#""Npm""#);
282    }
283
284    #[test]
285    fn test_deserialization() {
286        let json = r#""Npm""#;
287        let pt: PackageType = serde_json::from_str(json).unwrap();
288        assert_eq!(pt, PackageType::Npm);
289    }
290
291    #[test]
292    fn test_as_str() {
293        assert_eq!(PackageType::Npm.as_str(), "npm");
294        assert_eq!(PackageType::Cargo.as_str(), "cargo");
295        assert_eq!(PackageType::Pypi.as_str(), "pypi");
296        assert_eq!(PackageType::Alpm.as_str(), "alpm");
297        assert_eq!(PackageType::Vcpkg.as_str(), "vcpkg");
298        assert_eq!(PackageType::Hackage.as_str(), "hackage");
299        assert_eq!(PackageType::Hex.as_str(), "hex");
300    }
301
302    #[test]
303    fn test_display() {
304        assert_eq!(PackageType::Npm.to_string(), "npm");
305    }
306
307    #[test]
308    fn test_as_ref() {
309        let pt = PackageType::Npm;
310        let s: &str = pt.as_ref();
311        assert_eq!(s, "npm");
312    }
313
314    #[test]
315    fn test_kebab_case_variants() {
316        assert_eq!(PackageType::JbossService.as_str(), "jboss-service");
317        assert_eq!(PackageType::LinuxDistro.as_str(), "linux-distro");
318        assert_eq!(PackageType::PnpmLock.as_str(), "pnpm-lock");
319        assert_eq!(PackageType::Winexe.as_str(), "winexe");
320        assert_eq!(PackageType::WindowsUpdate.as_str(), "windows-update");
321
322        let json = serde_json::to_string(&PackageType::JbossService).unwrap();
323        assert_eq!(json, r#""JbossService""#);
324
325        let json = serde_json::to_string(&PackageType::LinuxDistro).unwrap();
326        assert_eq!(json, r#""LinuxDistro""#);
327
328        let json = serde_json::to_string(&PackageType::PnpmLock).unwrap();
329        assert_eq!(json, r#""PnpmLock""#);
330
331        let json = serde_json::to_string(&PackageType::Winexe).unwrap();
332        assert_eq!(json, r#""Winexe""#);
333
334        let json = serde_json::to_string(&PackageType::WindowsUpdate).unwrap();
335        assert_eq!(json, r#""WindowsUpdate""#);
336    }
337
338    #[test]
339    fn test_snake_case_variant() {
340        assert_eq!(PackageType::AndroidLib.as_str(), "android_lib");
341
342        let json = serde_json::to_string(&PackageType::AndroidLib).unwrap();
343        assert_eq!(json, r#""AndroidLib""#);
344    }
345
346    #[test]
347    fn test_deserialization_kebab_case() {
348        let pt: PackageType = serde_json::from_str(r#""JbossService""#).unwrap();
349        assert_eq!(pt, PackageType::JbossService);
350
351        let pt: PackageType = serde_json::from_str(r#""LinuxDistro""#).unwrap();
352        assert_eq!(pt, PackageType::LinuxDistro);
353
354        let pt: PackageType = serde_json::from_str(r#""PnpmLock""#).unwrap();
355        assert_eq!(pt, PackageType::PnpmLock);
356
357        let pt: PackageType = serde_json::from_str(r#""Winexe""#).unwrap();
358        assert_eq!(pt, PackageType::Winexe);
359
360        let pt: PackageType = serde_json::from_str(r#""WindowsUpdate""#).unwrap();
361        assert_eq!(pt, PackageType::WindowsUpdate);
362    }
363}