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