1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::{NpmAccess, RegistryKey, VersionGrammar};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
8#[serde(rename_all = "lowercase")]
9#[non_exhaustive]
10pub enum Ecosystem {
11 Cargo,
12 Npm,
13 Pypi,
15 Go,
16 Maven,
17 NuGet,
18 Deno,
19 Jsr,
20}
21
22impl Ecosystem {
23 pub fn prefix(&self) -> &'static str {
24 match self {
25 Ecosystem::Cargo => "cargo",
26 Ecosystem::Npm => "npm",
27 Ecosystem::Pypi => "pypi",
28 Ecosystem::Go => "go",
29 Ecosystem::Maven => "maven",
30 Ecosystem::NuGet => "nuget",
31 Ecosystem::Deno => "deno",
32 Ecosystem::Jsr => "jsr",
33 }
34 }
35
36 pub fn from_prefix(s: &str) -> Option<Self> {
37 match s.to_ascii_lowercase().as_str() {
38 "cargo" => Some(Ecosystem::Cargo),
39 "npm" => Some(Ecosystem::Npm),
40 "pypi" => Some(Ecosystem::Pypi),
41 "go" => Some(Ecosystem::Go),
42 "maven" => Some(Ecosystem::Maven),
43 "nuget" => Some(Ecosystem::NuGet),
44 "deno" => Some(Ecosystem::Deno),
45 "jsr" => Some(Ecosystem::Jsr),
46 _ => None,
47 }
48 }
49
50 pub fn version_grammar(&self) -> VersionGrammar {
51 match self {
52 Ecosystem::Cargo | Ecosystem::Npm | Ecosystem::Deno | Ecosystem::Jsr | Ecosystem::NuGet | Ecosystem::Go => {
53 VersionGrammar::SemVer
54 }
55 Ecosystem::Pypi => VersionGrammar::Pep440,
56 Ecosystem::Maven => VersionGrammar::Maven,
57 }
58 }
59
60 pub fn is_implemented(&self) -> bool {
61 matches!(self, Ecosystem::Cargo | Ecosystem::Npm | Ecosystem::Pypi)
62 }
63}
64
65#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
67#[serde(rename_all = "camelCase")]
68#[non_exhaustive]
69pub enum PublishTarget {
70 CratesIo,
71 Npm {
72 registry: Option<String>,
73 access: Option<NpmAccess>,
81 },
82 Pypi {
83 index: Option<String>,
84 },
85 NuGet {
86 source: Option<String>,
87 },
88 GitHubRelease,
89 None,
90}
91
92impl PublishTarget {
93 pub fn registry_key(&self) -> Option<RegistryKey> {
94 match self {
95 PublishTarget::CratesIo => Some(RegistryKey(RegistryKey::CRATES_IO.to_string())),
96 PublishTarget::Npm { .. } => Some(RegistryKey(RegistryKey::NPM.to_string())),
97 PublishTarget::Pypi { .. } => Some(RegistryKey(RegistryKey::PYPI.to_string())),
98 PublishTarget::NuGet { .. } => Some(RegistryKey(RegistryKey::NUGET.to_string())),
99 PublishTarget::GitHubRelease | PublishTarget::None => None,
100 }
101 }
102
103 pub fn ecosystem(&self) -> Option<Ecosystem> {
104 match self {
105 PublishTarget::CratesIo => Some(Ecosystem::Cargo),
106 PublishTarget::Npm { .. } => Some(Ecosystem::Npm),
107 PublishTarget::Pypi { .. } => Some(Ecosystem::Pypi),
108 PublishTarget::NuGet { .. } => Some(Ecosystem::NuGet),
109 PublishTarget::GitHubRelease | PublishTarget::None => None,
110 }
111 }
112
113 pub fn config_str(&self) -> &'static str {
118 match self {
119 PublishTarget::CratesIo => "crates-io",
120 PublishTarget::Npm { .. } => "npm",
121 PublishTarget::Pypi { .. } => "pypi",
122 PublishTarget::NuGet { .. } => "nuget",
123 PublishTarget::GitHubRelease => "github-release",
124 PublishTarget::None => "none",
125 }
126 }
127}
128
129pub fn normalize_pypi_package_name(name: &str) -> String {
139 name.to_lowercase()
140 .split(['-', '.', '_'])
141 .filter(|s| !s.is_empty())
142 .collect::<Vec<_>>()
143 .join("_")
144}
145
146#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
148#[serde(rename_all = "lowercase")]
149pub enum ReleaseTrigger {
150 #[default]
151 Changeset,
152 Auto,
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn maps_ecosystem_prefixes() {
161 assert_eq!(Ecosystem::from_prefix("cargo"), Some(Ecosystem::Cargo));
162 assert_eq!(Ecosystem::from_prefix("npm"), Some(Ecosystem::Npm));
163 assert_eq!(Ecosystem::Cargo.prefix(), "cargo");
164 }
165
166 #[test]
167 fn identifies_implemented_ecosystems() {
168 assert!(Ecosystem::Cargo.is_implemented());
169 assert!(Ecosystem::Npm.is_implemented());
170 assert!(Ecosystem::Pypi.is_implemented());
171 }
172
173 #[test]
174 fn normalize_pypi_package_name_treats_hyphen_underscore_dot_as_equivalent() {
175 assert_eq!(normalize_pypi_package_name("my-package"), "my_package");
176 assert_eq!(normalize_pypi_package_name("my_package"), "my_package");
177 assert_eq!(normalize_pypi_package_name("my.package"), "my_package");
178 assert_eq!(normalize_pypi_package_name("My--Package"), "my_package");
179 assert_eq!(normalize_pypi_package_name("MY.PACKAGE"), "my_package");
180 }
181
182 #[test]
183 fn normalize_pypi_package_name_collapses_mixed_runs() {
184 assert_eq!(normalize_pypi_package_name("foo-.-bar"), "foo_bar");
185 assert_eq!(normalize_pypi_package_name("foo___bar"), "foo_bar");
186 }
187}