qtcloud_devops_cli/code/
status.rs1use std::path::PathBuf;
2
3use super::model::{ComponentStatus, StatusReport, SyncStatus};
4use crate::git::submodule::{RepoState, SubmoduleStatus};
5
6fn map_status(s: &SubmoduleStatus) -> SyncStatus {
7 match s {
8 SubmoduleStatus::Clean => SyncStatus::Synced,
9 SubmoduleStatus::AheadOfParent => SyncStatus::PendingPush,
10 SubmoduleStatus::BehindRemote => SyncStatus::PendingPull,
11 _ => SyncStatus::Conflict,
12 }
13}
14
15pub fn status(root: PathBuf, offline: bool) -> Result<StatusReport, String> {
16 let state = if offline {
17 RepoState::scan_offline(&root)
18 } else {
19 RepoState::scan(&root)
20 }
21 .map_err(|e| format!("扫描失败: {}", e))?;
22
23 let mut components = Vec::with_capacity(state.submodules.len());
24 for sm in &state.submodules {
25 let s = map_status(&sm.status);
26 components.push(ComponentStatus {
27 name: sm.name.clone(),
28 status: s,
29 ahead: sm.ahead_count,
30 behind: sm.behind_count,
31 });
32 }
33
34 let total = components.len();
35 let synced = components.iter().filter(|c| c.status == SyncStatus::Synced).count();
36 let pending = total - synced;
37
38 Ok(StatusReport {
39 root: state.root_path.to_string_lossy().to_string(),
40 components,
41 total,
42 synced,
43 pending,
44 })
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use crate::git::submodule::SubmoduleStatus;
51
52 #[test] fn test_map_clean() { assert_eq!(map_status(&SubmoduleStatus::Clean), SyncStatus::Synced); }
55 #[test] fn test_map_ahead() { assert_eq!(map_status(&SubmoduleStatus::AheadOfParent), SyncStatus::PendingPush); }
56 #[test] fn test_map_behind() { assert_eq!(map_status(&SubmoduleStatus::BehindRemote), SyncStatus::PendingPull); }
57 #[test] fn test_map_detached() { assert_eq!(map_status(&SubmoduleStatus::Detached), SyncStatus::Conflict); }
58 #[test] fn test_map_dirty() { assert_eq!(map_status(&SubmoduleStatus::Dirty), SyncStatus::Conflict); }
59 #[test] fn test_map_orphaned() { assert_eq!(map_status(&SubmoduleStatus::Orphaned), SyncStatus::Conflict); }
60 #[test] fn test_map_uninitialized() { assert_eq!(map_status(&SubmoduleStatus::Uninitialized), SyncStatus::Conflict); }
61
62 fn git_init(path: &std::path::Path) {
65 std::process::Command::new("git").args(["init", "-b", "main"]).current_dir(path).output().unwrap();
66 std::process::Command::new("git").args(["config", "user.email", "t@t"]).current_dir(path).output().unwrap();
67 std::process::Command::new("git").args(["config", "user.name", "t"]).current_dir(path).output().unwrap();
68 }
69
70 fn git_commit(path: &std::path::Path, msg: &str) {
71 std::fs::write(path.join("f"), msg).unwrap();
72 std::process::Command::new("git").args(["add", "."]).current_dir(path).output().unwrap();
73 std::process::Command::new("git").args(["commit", "-m", msg]).current_dir(path).output().unwrap();
74 }
75
76 #[test]
77 fn test_status_non_git_dir() {
78 let d = tempfile::tempdir().unwrap();
79 assert!(status(d.path().to_path_buf(), false).is_err());
80 }
81
82 #[test]
83 fn test_status_empty_repo() {
84 let d = tempfile::tempdir().unwrap();
85 git_init(d.path());
86 git_commit(d.path(), "init");
87 let report = status(d.path().to_path_buf(), false).unwrap();
88 assert_eq!(report.total, 0);
89 assert_eq!(report.synced, 0);
90 assert_eq!(report.pending, 0);
91 }
92
93 #[test]
94 fn test_status_with_synced_submodule() {
95 let tmp = tempfile::tempdir().unwrap();
96 let parent = tmp.path().join("parent");
97 let sub = tmp.path().join("sub");
98 std::fs::create_dir_all(&sub).unwrap(); git_init(&sub); git_commit(&sub, "init sub");
99 std::fs::create_dir_all(&parent).unwrap(); git_init(&parent); git_commit(&parent, "init parent");
100 std::process::Command::new("git").args(["submodule", "add", &sub.to_string_lossy(), "libs/sub"]).current_dir(&parent).output().unwrap();
101 std::process::Command::new("git").args(["commit", "-m", "add submodule"]).current_dir(&parent).output().unwrap();
102 let report = status(parent, false).unwrap();
103 assert_eq!(report.total, 1);
104 assert_eq!(report.components[0].status, SyncStatus::Synced);
105 }
106
107 #[test]
108 fn test_status_offline_flag() {
109 let d = tempfile::tempdir().unwrap();
110 git_init(d.path()); git_commit(d.path(), "init");
111 assert!(status(d.path().to_path_buf(), true).is_ok());
113 }
114}