specfold_core/check/
scope.rs1use crate::corpus::Corpus;
5use crate::graph::Graph;
6use crate::workspace::vcs;
7use anyhow::{Result, anyhow};
8use std::collections::BTreeSet;
9
10#[derive(Debug, Clone)]
11pub enum Range {
12 All,
14 Staged,
16 Changed(String),
18}
19
20#[derive(Debug, Clone)]
21pub struct Scope {
22 pub range: Range,
23 pub paths: BTreeSet<String>,
25 pub touched: BTreeSet<usize>,
27 pub with_neighbours: BTreeSet<usize>,
29 pub base: Option<String>,
31}
32
33impl Scope {
34 pub fn build(c: &Corpus, g: &Graph, range: Range) -> Result<Self> {
35 let (paths, base) = match &range {
36 Range::All => (BTreeSet::new(), None),
37 Range::Staged => {
38 let p = vcs::staged(&c.cfg.root).ok_or_else(|| anyhow!("--staged needs a git repository"))?;
39 (p.into_iter().collect(), Some("HEAD".to_string()))
40 }
41 Range::Changed(base) => {
42 let p = vcs::changed_since(&c.cfg.root, base).ok_or_else(|| anyhow!("cannot diff against {base}"))?;
43 (p.into_iter().collect(), Some(base.clone()))
44 }
45 };
46 let mut touched = BTreeSet::new();
47 if !matches!(range, Range::All) {
48 for (i, d) in c.docs.iter().enumerate() {
49 let own = std::iter::once(&d.path).chain(&d.chapters).chain(&d.bundle_files);
50 if own.into_iter().any(|p| paths.contains(p)) {
51 touched.insert(i);
52 }
53 }
54 }
55 let mut with_neighbours = touched.clone();
56 for &i in &touched {
57 for e in g.edges_to(i) {
58 with_neighbours.insert(e.from);
59 }
60 }
61 Ok(Scope { range, paths, touched, with_neighbours, base })
62 }
63
64 pub fn is_all(&self) -> bool {
65 matches!(self.range, Range::All)
66 }
67
68 pub fn covers(&self, i: usize) -> bool {
70 self.is_all() || self.touched.contains(&i)
71 }
72
73 pub fn covers_links(&self, i: usize) -> bool {
75 self.is_all() || self.with_neighbours.contains(&i)
76 }
77
78 pub fn covers_path(&self, p: &str) -> bool {
79 self.is_all() || self.paths.contains(p)
80 }
81}