1use serde::{Deserialize, Serialize};
7use std::fmt;
8
9#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "lowercase")]
14pub enum VersioningStrategy {
15 Fixed,
20
21 Linked,
26
27 #[default]
32 Independent,
33}
34
35impl fmt::Display for VersioningStrategy {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 Self::Fixed => write!(f, "fixed"),
39 Self::Linked => write!(f, "linked"),
40 Self::Independent => write!(f, "independent"),
41 }
42 }
43}
44
45#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
49#[serde(rename_all = "lowercase")]
50pub enum TagType {
51 #[default]
53 Semver,
54
55 Calver,
57}
58
59impl fmt::Display for TagType {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 Self::Semver => write!(f, "semver"),
63 Self::Calver => write!(f, "calver"),
64 }
65 }
66}
67
68#[derive(Debug, Clone, Default, Serialize, Deserialize)]
70#[serde(default)]
71pub struct ReleaseConfig {
72 pub git: ReleaseGitConfig,
74 pub packages: ReleasePackagesConfig,
76 pub changelog: ChangelogConfig,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82#[serde(default)]
83pub struct ReleaseGitConfig {
84 #[serde(rename = "defaultBranch")]
86 pub default_branch: String,
87 #[serde(rename = "tagPrefix")]
89 pub tag_prefix: String,
90 #[serde(rename = "tagType")]
92 pub tag_type: TagType,
93 #[serde(rename = "createTags")]
95 pub create_tags: bool,
96 #[serde(rename = "pushTags")]
98 pub push_tags: bool,
99}
100
101impl Default for ReleaseGitConfig {
102 fn default() -> Self {
103 Self {
104 default_branch: "main".to_string(),
105 tag_prefix: String::new(),
106 tag_type: TagType::Semver,
107 create_tags: true,
108 push_tags: true,
109 }
110 }
111}
112
113impl ReleaseGitConfig {
114 #[must_use]
118 pub fn format_tag(&self, version: &str) -> String {
119 format!("{}{}", self.tag_prefix, version)
120 }
121}
122
123#[derive(Debug, Clone, Default, Serialize, Deserialize)]
125#[serde(default)]
126pub struct ReleasePackagesConfig {
127 pub strategy: VersioningStrategy,
129 pub fixed: Vec<Vec<String>>,
131 pub linked: Vec<Vec<String>>,
133}
134
135impl ReleasePackagesConfig {
136 #[must_use]
138 pub fn is_in_fixed_group(&self, package: &str) -> bool {
139 self.fixed
140 .iter()
141 .any(|group| group.contains(&package.to_string()))
142 }
143
144 #[must_use]
146 pub fn get_fixed_group(&self, package: &str) -> Option<&Vec<String>> {
147 self.fixed
148 .iter()
149 .find(|group| group.contains(&package.to_string()))
150 }
151
152 #[must_use]
154 pub fn is_in_linked_group(&self, package: &str) -> bool {
155 self.linked
156 .iter()
157 .any(|group| group.contains(&package.to_string()))
158 }
159
160 #[must_use]
162 pub fn get_linked_group(&self, package: &str) -> Option<&Vec<String>> {
163 self.linked
164 .iter()
165 .find(|group| group.contains(&package.to_string()))
166 }
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171#[serde(default)]
172pub struct ChangelogConfig {
173 pub path: String,
175 #[serde(rename = "perPackage")]
177 pub per_package: bool,
178 pub workspace: bool,
180}
181
182impl Default for ChangelogConfig {
183 fn default() -> Self {
184 Self {
185 path: "CHANGELOG.md".to_string(),
186 per_package: true,
187 workspace: true,
188 }
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn test_release_config_default() {
198 let config = ReleaseConfig::default();
199 assert_eq!(config.git.default_branch, "main");
200 assert_eq!(config.git.tag_prefix, "");
201 assert_eq!(config.git.tag_type, TagType::Semver);
202 assert!(config.git.create_tags);
203 assert!(config.git.push_tags);
204 }
205
206 #[test]
207 fn test_git_config_format_tag() {
208 let config = ReleaseGitConfig::default();
210 assert_eq!(config.format_tag("1.0.0"), "1.0.0");
211
212 let v_config = ReleaseGitConfig {
214 tag_prefix: "v".to_string(),
215 ..Default::default()
216 };
217 assert_eq!(v_config.format_tag("1.0.0"), "v1.0.0");
218
219 let pkg_config = ReleaseGitConfig {
221 tag_prefix: "vscode/v".to_string(),
222 ..Default::default()
223 };
224 assert_eq!(pkg_config.format_tag("0.1.1"), "vscode/v0.1.1");
225 }
226
227 #[test]
228 fn test_tag_type_default() {
229 assert_eq!(TagType::default(), TagType::Semver);
230 }
231
232 #[test]
233 fn test_packages_config_fixed_groups() {
234 let config = ReleasePackagesConfig {
235 fixed: vec![
236 vec!["pkg-a".to_string(), "pkg-b".to_string()],
237 vec!["pkg-c".to_string()],
238 ],
239 ..Default::default()
240 };
241
242 assert!(config.is_in_fixed_group("pkg-a"));
243 assert!(config.is_in_fixed_group("pkg-b"));
244 assert!(config.is_in_fixed_group("pkg-c"));
245 assert!(!config.is_in_fixed_group("pkg-d"));
246
247 let group = config.get_fixed_group("pkg-a").unwrap();
248 assert!(group.contains(&"pkg-a".to_string()));
249 assert!(group.contains(&"pkg-b".to_string()));
250 }
251
252 #[test]
253 fn test_packages_config_linked_groups() {
254 let config = ReleasePackagesConfig {
255 linked: vec![vec!["pkg-x".to_string(), "pkg-y".to_string()]],
256 ..Default::default()
257 };
258
259 assert!(config.is_in_linked_group("pkg-x"));
260 assert!(config.is_in_linked_group("pkg-y"));
261 assert!(!config.is_in_linked_group("pkg-z"));
262 }
263
264 #[test]
265 fn test_changelog_config_default() {
266 let config = ChangelogConfig::default();
267 assert_eq!(config.path, "CHANGELOG.md");
268 assert!(config.per_package);
269 assert!(config.workspace);
270 }
271
272 #[test]
273 fn test_config_serialization() {
274 let config = ReleaseConfig::default();
275 let json = serde_json::to_string(&config).unwrap();
276 let parsed: ReleaseConfig = serde_json::from_str(&json).unwrap();
277 assert_eq!(parsed.git.default_branch, config.git.default_branch);
278 }
279}