Skip to main content

outpost_core/ops/
prune.rs

1use 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}