specfold_core/graph/
query.rs1use super::types::Graph;
5use crate::corpus::Corpus;
6use crate::workspace::owners::matcher;
7use std::collections::{BTreeSet, VecDeque};
8
9#[derive(Debug, Clone)]
11pub struct Governing {
12 pub idx: usize,
13 pub pattern: String,
14 pub specificity: usize,
15}
16
17pub fn governing(c: &Corpus, path: &str) -> Vec<Governing> {
20 let path = path.trim_start_matches("./").trim_end_matches('/');
21 let mut out = vec![];
22 for (i, d) in c.docs.iter().enumerate() {
23 let globs = d.rel().remove("governs").unwrap_or_default();
24 if globs.is_empty() {
25 let Some(owner) = c.owners.by_id(&d.id.owner) else { continue };
26 if !owner.path.is_empty() && (path == owner.path || path.starts_with(&format!("{}/", owner.path))) {
27 out.push(Governing { idx: i, pattern: format!("{}/**", owner.path), specificity: owner.path.len() });
28 }
29 continue;
30 }
31 for g in globs {
32 if matcher(&g).is_some_and(|m| m.is_match(path)) {
33 let specificity = g.split(['*', '?', '[', '{']).next().unwrap_or("").len();
34 out.push(Governing { idx: i, pattern: g, specificity });
35 break;
36 }
37 }
38 }
39 out.sort_by(|a, b| b.specificity.cmp(&a.specificity).then(a.idx.cmp(&b.idx)));
40 out
41}
42
43pub fn impact(g: &Graph, idx: usize) -> Vec<(usize, usize)> {
45 let mut seen = BTreeSet::from([idx]);
46 let mut out = vec![];
47 let mut q = VecDeque::from([(idx, 0usize)]);
48 while let Some((cur, depth)) = q.pop_front() {
49 for e in g.edges_to(cur) {
50 if seen.insert(e.from) {
51 out.push((e.from, depth + 1));
52 q.push_back((e.from, depth + 1));
53 }
54 }
55 }
56 out
57}
58
59pub fn supersede_cycle(g: &Graph, idx: usize) -> Option<Vec<usize>> {
62 let mut stack = vec![(idx, vec![idx])];
63 let mut seen = BTreeSet::new();
64 while let Some((cur, path)) = stack.pop() {
65 for e in g.edges_from(cur).filter(|e| e.kind == "supersedes") {
66 if e.to == idx {
67 return Some(path);
68 }
69 if seen.insert(e.to) {
70 let mut next = path.clone();
71 next.push(e.to);
72 stack.push((e.to, next));
73 }
74 }
75 }
76 None
77}
78
79pub fn chain(g: &Graph, idx: usize) -> Vec<usize> {
81 let mut path = vec![idx];
82 let mut cur = idx;
83 loop {
84 let next = g.edges_to(cur).find(|e| e.kind == "supersedes").map(|e| e.from);
85 match next {
86 Some(n) if !path.contains(&n) => {
87 path.push(n);
88 cur = n;
89 }
90 _ => return path,
91 }
92 }
93}