outpost_core/ops/
prune.rs1use std::path::{Path, PathBuf};
2
3use crate::{Outpost, OutpostError, OutpostResult, SourceRepo};
4
5pub struct PruneOptions {
6 pub dry_run: bool,
7 pub verbose: bool,
8}
9
10pub struct PruneReport {
11 pub removed_entries: Vec<PathBuf>,
12 pub orphaned_source_missing: Vec<PathBuf>,
13 pub locked_entries: Vec<PathBuf>,
14 pub dry_run: bool,
15}
16
17pub fn run(source: &SourceRepo, opts: PruneOptions) -> OutpostResult<PruneReport> {
18 let mut registry = source.registry_mut()?;
19 let entries = registry.entries().to_vec();
20 let mut report = PruneReport {
21 removed_entries: Vec::new(),
22 orphaned_source_missing: Vec::new(),
23 locked_entries: Vec::new(),
24 dry_run: opts.dry_run,
25 };
26
27 for entry in entries {
28 if entry.locked {
29 report.locked_entries.push(entry.path);
30 } else if !entry.path.exists() {
31 report.removed_entries.push(entry.path.clone());
32 if !opts.dry_run {
33 registry.remove_by_path(&entry.path)?;
34 }
35 } else if source_missing_outpost(&entry.path)? {
36 report.orphaned_source_missing.push(entry.path);
37 }
38 }
39
40 if !opts.dry_run && !report.removed_entries.is_empty() {
41 registry.save()?;
42 }
43
44 let _ = opts.verbose;
45 Ok(report)
46}
47
48fn source_missing_outpost(path: &Path) -> OutpostResult<bool> {
49 if !path.is_dir() {
50 return Ok(false);
51 }
52
53 match Outpost::at(path).and_then(|outpost| outpost.source_repo()) {
54 Ok(_) => Ok(false),
55 Err(OutpostError::SourceMissing(_)) => Ok(true),
56 Err(OutpostError::NotARepo(_))
57 | Err(OutpostError::NotAnOutpost(_))
58 | Err(OutpostError::RegistryEntryNotManaged(_))
59 | Err(OutpostError::GitFailed { .. }) => Ok(false),
60 Err(err) => Err(err),
61 }
62}