Skip to main content

provenant/models/
package_type.rs

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