1use std::path::PathBuf;
2
3use crate::outpost_id::{OutpostId, shortest_unique_prefixes};
4use crate::{AheadBehind, BranchName, OutpostError, OutpostResult, SourceRepo};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct OutpostSummary {
8 pub display_id: String,
9 pub path: PathBuf,
10 pub current_branch: Option<BranchName>,
11 pub state: OutpostState,
12 pub ahead_behind: Option<AheadBehind>,
13 pub locked: bool,
14 pub lock_reason: Option<String>,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum OutpostState {
19 Clean,
20 Dirty,
21 Missing,
22 NotManaged,
23}
24
25pub fn run(source: &SourceRepo) -> OutpostResult<Vec<OutpostSummary>> {
26 let registry = source.registry()?;
27 let ids = registry
28 .entries()
29 .iter()
30 .map(|entry| OutpostId::derive(source.work_tree(), &entry.path))
31 .collect::<Vec<_>>();
32 let prefixes = shortest_unique_prefixes(ids.iter());
33 registry
34 .entries()
35 .iter()
36 .zip(prefixes)
37 .map(|(entry, display_id)| summarize_entry(source, entry, display_id))
38 .collect()
39}
40
41fn summarize_entry(
42 source: &SourceRepo,
43 entry: &crate::RegistryEntry,
44 display_id: String,
45) -> OutpostResult<OutpostSummary> {
46 let mut summary = OutpostSummary {
47 display_id,
48 path: entry.path.clone(),
49 current_branch: None,
50 state: OutpostState::Missing,
51 ahead_behind: None,
52 locked: entry.locked,
53 lock_reason: entry.lock_reason.clone(),
54 };
55
56 if !entry.path.exists() {
57 return Ok(summary);
58 }
59
60 let outpost = match crate::safety::check_entry_is_managed_outpost_of(source, entry) {
61 Ok(outpost) => outpost,
62 Err(OutpostError::RegistryEntryNotManaged(_)) => {
63 summary.state = OutpostState::NotManaged;
64 return Ok(summary);
65 }
66 Err(err) => return Err(err),
67 };
68
69 summary.current_branch = outpost.current_branch().ok();
70 summary.ahead_behind = outpost.ahead_behind_source().ok();
71 summary.state = if outpost.is_dirty()? {
72 OutpostState::Dirty
73 } else {
74 OutpostState::Clean
75 };
76
77 Ok(summary)
78}