1#[derive(Debug, Clone, PartialEq)]
7pub enum ReleaseStatus {
8 Unreleased,
10 Latest,
12 Pending,
14 Inconsistent,
16 Unknown,
18}
19
20impl std::fmt::Display for ReleaseStatus {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 Self::Unreleased => write!(f, "未发布"),
33 Self::Latest => write!(f, "已是最新"),
34 Self::Pending => write!(f, "待发布"),
35 Self::Inconsistent => write!(f, "版本冲突"),
36 Self::Unknown => write!(f, "状态未知"),
37 }
38 }
39}
40
41#[derive(Debug)]
47pub struct ReleaseState {
48 pub status: ReleaseStatus,
50 pub scope: String,
52 pub scope_path: String,
54 pub current_version: Option<String>,
56 pub pending_commits: usize,
58 pub changelog: String,
60 pub version_consistent: Option<bool>,
62}
63
64impl ReleaseState {
65 pub fn builder() -> ReleaseStateBuilder {
82 ReleaseStateBuilder::default()
83 }
84}
85
86#[derive(Debug)]
92pub struct ReleaseStateBuilder {
93 status: Option<ReleaseStatus>,
94 scope: Option<String>,
95 scope_path: Option<String>,
96 current_version: Option<String>,
97 pending_commits: usize,
98 version_consistent: Option<bool>,
99 changelog: String,
100}
101
102impl Default for ReleaseStateBuilder {
103 fn default() -> Self {
104 Self {
105 status: None,
106 scope: None,
107 scope_path: None,
108 current_version: None,
109 pending_commits: 0,
110 version_consistent: None,
111 changelog: "CHANGELOG.md".into(),
112 }
113 }
114}
115
116impl ReleaseStateBuilder {
117 pub fn status(mut self, value: ReleaseStatus) -> Self {
119 self.status = Some(value);
120 self
121 }
122
123 pub fn scope(mut self, value: impl Into<String>) -> Self {
125 self.scope = Some(value.into());
126 self
127 }
128
129 pub fn scope_path(mut self, value: impl Into<String>) -> Self {
131 self.scope_path = Some(value.into());
132 self
133 }
134
135 pub fn current_version(mut self, value: impl Into<String>) -> Self {
137 self.current_version = Some(value.into());
138 self
139 }
140
141 pub fn pending_commits(mut self, value: usize) -> Self {
143 self.pending_commits = value;
144 self
145 }
146
147 pub fn version_consistent(mut self, value: bool) -> Self {
149 self.version_consistent = Some(value);
150 self
151 }
152
153 pub fn changelog(mut self, value: impl Into<String>) -> Self {
155 self.changelog = value.into();
156 self
157 }
158
159 pub fn build(self) -> ReleaseState {
165 ReleaseState {
166 status: self.status.expect("ReleaseStateBuilder: status 是必需的"),
167 scope: self.scope.expect("ReleaseStateBuilder: scope 是必需的"),
168 scope_path: self
169 .scope_path
170 .expect("ReleaseStateBuilder: scope_path 是必需的"),
171 current_version: self.current_version,
172 pending_commits: self.pending_commits,
173 changelog: self.changelog,
174 version_consistent: self.version_consistent,
175 }
176 }
177}
178
179impl std::fmt::Display for ReleaseState {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 writeln!(f, " [{}]", self.scope)?;
193 writeln!(f, " 状态: {}", self.status)?;
194 writeln!(f, " 路径: {}", self.scope_path)?;
195 match &self.current_version {
196 Some(v) => writeln!(f, " 最新版本: {}", v)?,
197 None => writeln!(f, " 最新版本: (无)")?,
198 }
199 writeln!(f, " 未发布提交: {}", self.pending_commits)?;
200 writeln!(f, " 变更日志: {}", self.changelog)?;
201 match self.version_consistent {
202 Some(true) => writeln!(f, " 版本一致: 是"),
203 Some(false) => writeln!(f, " 版本一致: 否"),
204 None => Ok(()),
205 }
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212
213 #[test]
216 fn test_release_status_variants() {
217 assert_ne!(ReleaseStatus::Unreleased, ReleaseStatus::Latest);
218 assert_ne!(ReleaseStatus::Latest, ReleaseStatus::Pending);
219 assert_ne!(ReleaseStatus::Pending, ReleaseStatus::Inconsistent);
220 assert_ne!(ReleaseStatus::Inconsistent, ReleaseStatus::Unknown);
221 }
222
223 #[test]
224 fn test_release_status_debug() {
225 let s = format!("{:?}", ReleaseStatus::Unreleased);
226 assert_eq!(s, "Unreleased");
227 }
228
229 #[test]
230 fn test_release_status_display() {
231 assert_eq!(format!("{}", ReleaseStatus::Unreleased), "未发布");
232 assert_eq!(format!("{}", ReleaseStatus::Latest), "已是最新");
233 assert_eq!(format!("{}", ReleaseStatus::Pending), "待发布");
234 assert_eq!(format!("{}", ReleaseStatus::Inconsistent), "版本冲突");
235 assert_eq!(format!("{}", ReleaseStatus::Unknown), "状态未知");
236 }
237
238 #[test]
241 fn test_release_state_unreleased() {
242 let state = ReleaseState {
243 status: ReleaseStatus::Unreleased,
244 scope: "cli".into(),
245 scope_path: "src/cli".into(),
246 current_version: None,
247 pending_commits: 0,
248 changelog: "CHANGELOG.md".into(),
249 version_consistent: None,
250 };
251 assert_eq!(state.status, ReleaseStatus::Unreleased);
252 assert!(state.current_version.is_none());
253 assert_eq!(state.pending_commits, 0);
254 }
255
256 #[test]
257 fn test_release_state_pending() {
258 let state = ReleaseState {
259 status: ReleaseStatus::Pending,
260 scope: "core".into(),
261 scope_path: "packages/core".into(),
262 current_version: Some("v1.2.3".into()),
263 pending_commits: 5,
264 changelog: "CHANGELOG.md".into(),
265 version_consistent: Some(true),
266 };
267 assert_eq!(state.status, ReleaseStatus::Pending);
268 assert_eq!(state.current_version.as_deref(), Some("v1.2.3"));
269 assert_eq!(state.pending_commits, 5);
270 assert_eq!(state.version_consistent, Some(true));
271 }
272
273 #[test]
274 fn test_release_state_latest() {
275 let state = ReleaseState {
276 status: ReleaseStatus::Latest,
277 scope: "(root)".into(),
278 scope_path: ".".into(),
279 current_version: Some("v5.0.0".into()),
280 pending_commits: 0,
281 changelog: "CHANGELOG.md".into(),
282 version_consistent: Some(true),
283 };
284 assert_eq!(state.status, ReleaseStatus::Latest);
285 assert_eq!(state.pending_commits, 0);
286 }
287
288 #[test]
289 fn test_release_state_inconsistent() {
290 let state = ReleaseState {
291 status: ReleaseStatus::Inconsistent,
292 scope: "web".into(),
293 scope_path: "src/web".into(),
294 current_version: Some("v2.0.0".into()),
295 pending_commits: 3,
296 changelog: "CHANGELOG.md".into(),
297 version_consistent: Some(false),
298 };
299 assert_eq!(state.status, ReleaseStatus::Inconsistent);
300 assert_eq!(state.version_consistent, Some(false));
301 }
302
303 #[test]
304 fn test_release_state_unknown() {
305 let state = ReleaseState {
306 status: ReleaseStatus::Unknown,
307 scope: "service".into(),
308 scope_path: "apps/service".into(),
309 current_version: None,
310 pending_commits: 0,
311 changelog: "CHANGELOG.md".into(),
312 version_consistent: None,
313 };
314 assert_eq!(state.status, ReleaseStatus::Unknown);
315 }
316
317 #[test]
318 fn test_release_state_builder_minimal() {
319 let s = ReleaseState::builder()
320 .status(ReleaseStatus::Unreleased)
321 .scope("cli")
322 .scope_path("src/cli")
323 .build();
324 assert_eq!(s.scope, "cli");
325 assert_eq!(s.scope_path, "src/cli");
326 assert_eq!(s.status, ReleaseStatus::Unreleased);
327 assert!(s.current_version.is_none());
328 assert_eq!(s.pending_commits, 0);
329 assert_eq!(s.changelog, "CHANGELOG.md");
330 assert!(s.version_consistent.is_none());
331 }
332
333 #[test]
334 fn test_release_state_builder_full() {
335 let s = ReleaseState::builder()
336 .status(ReleaseStatus::Pending)
337 .scope("(root)")
338 .scope_path(".")
339 .current_version("v2.0.0")
340 .pending_commits(5)
341 .version_consistent(false)
342 .build();
343 assert_eq!(s.scope, "(root)");
344 assert_eq!(s.current_version.as_deref(), Some("v2.0.0"));
345 assert_eq!(s.pending_commits, 5);
346 assert_eq!(s.version_consistent, Some(false));
347 }
348
349 #[test]
350 fn test_release_state_display() {
351 let state = ReleaseState {
352 status: ReleaseStatus::Latest,
353 scope: "qtcloud-core".into(),
354 scope_path: "apps/qtcloud-core".into(),
355 current_version: Some("v2.1.0".into()),
356 pending_commits: 0,
357 changelog: "CHANGELOG.md".into(),
358 version_consistent: Some(true),
359 };
360 assert_eq!(
361 format!("{}", state),
362 " [qtcloud-core]\n 状态: 已是最新\n 路径: apps/qtcloud-core\n 最新版本: v2.1.0\n 未发布提交: 0\n 变更日志: CHANGELOG.md\n 版本一致: 是\n"
363 );
364
365 let state = ReleaseState {
366 status: ReleaseStatus::Unreleased,
367 scope: "(root)".into(),
368 scope_path: ".".into(),
369 current_version: None,
370 pending_commits: 0,
371 changelog: "CHANGELOG.md".into(),
372 version_consistent: None,
373 };
374 assert_eq!(
375 format!("{}", state),
376 " [(root)]\n 状态: 未发布\n 路径: .\n 最新版本: (无)\n 未发布提交: 0\n 变更日志: CHANGELOG.md\n"
377 );
378
379 let state = ReleaseState {
380 status: ReleaseStatus::Inconsistent,
381 scope: "web".into(),
382 scope_path: "src/web".into(),
383 current_version: Some("v2.0.0".into()),
384 pending_commits: 3,
385 changelog: "CHANGELOG.md".into(),
386 version_consistent: Some(false),
387 };
388 assert_eq!(
389 format!("{}", state),
390 " [web]\n 状态: 版本冲突\n 路径: src/web\n 最新版本: v2.0.0\n 未发布提交: 3\n 变更日志: CHANGELOG.md\n 版本一致: 否\n"
391 );
392 }
393}