Skip to main content

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