specfold_core/graph/
build.rs1use super::types::{DOC_RELATIONS, Edge, Graph, MENTIONS, Resolution};
4use crate::corpus::Corpus;
5use crate::model::RefText;
6
7impl Graph {
8 pub fn build(c: &Corpus) -> Self {
9 let mut g = Graph {
10 anchors: vec![Default::default(); c.docs.len()],
11 outgoing: vec![vec![]; c.docs.len()],
12 incoming: vec![vec![]; c.docs.len()],
13 ..Default::default()
14 };
15 for (i, d) in c.docs.iter().enumerate() {
16 g.by_id.insert(d.id.to_string(), i);
17 g.by_path.insert(d.path.clone(), i);
18 if let Some(dir) = &d.dir {
19 g.by_path.insert(dir.clone(), i);
20 }
21 if let Some(s) = d.slug_id() {
22 g.by_slug.entry(s).or_default().push(i);
23 }
24 for a in d.list_field("aliases") {
25 g.by_alias.entry(a).or_default().push(i);
26 }
27 g.anchors[i] = d.anchors.iter().map(|a| a.item.clone()).collect();
28 }
29 for (old, news) in &c.aliases.map {
30 for n in news {
31 if let Some(&i) = g.by_id.get(n) {
32 let list = g.by_alias.entry(old.clone()).or_default();
33 if !list.contains(&i) {
34 list.push(i);
35 }
36 }
37 }
38 }
39 for (i, d) in c.docs.iter().enumerate() {
40 for (kind, targets) in d.rel() {
41 if !DOC_RELATIONS.contains(&kind.as_str()) {
42 continue;
43 }
44 for t in targets {
45 g.link(i, &t, &kind);
46 }
47 }
48 for l in &d.links {
49 g.link(i, &l.item, MENTIONS);
50 }
51 }
52 g
53 }
54
55 fn link(&mut self, from: usize, text: &str, kind: &str) {
56 if let Resolution::Doc { idx, anchor, .. } = self.resolve(text) {
57 if idx == from {
58 return;
59 }
60 let e = self.edges.len();
61 self.edges.push(Edge { from, to: idx, kind: kind.to_string(), anchor });
62 self.outgoing[from].push(e);
63 self.incoming[idx].push(e);
64 }
65 }
66
67 pub fn resolve(&self, text: &str) -> Resolution {
69 let r = RefText::parse(text);
70 let mut target = r.target.trim_end_matches('/');
73 while let Some(rest) = target.strip_prefix("../").or_else(|| target.strip_prefix("./")) {
74 target = rest;
75 }
76 let (candidates, via_alias) = if let Some(&i) = self.by_id.get(target).or_else(|| self.by_path.get(target)) {
77 (vec![i], false)
78 } else if let Some(list) = self.by_alias.get(target) {
79 (list.clone(), true)
80 } else if let Some(list) = self.by_slug.get(target) {
81 (list.clone(), true)
82 } else {
83 (vec![], false)
84 };
85 match candidates.as_slice() {
86 [] => Resolution::Missing,
87 [idx] => match r.anchor {
88 Some(a) if !self.anchors[*idx].contains(&a) => Resolution::MissingAnchor { idx: *idx, anchor: a },
89 anchor => Resolution::Doc { idx: *idx, anchor, via_alias },
90 },
91 many => Resolution::Ambiguous(many.to_vec()),
92 }
93 }
94
95 pub fn edges_from(&self, idx: usize) -> impl Iterator<Item = &Edge> {
96 self.outgoing[idx].iter().map(|&e| &self.edges[e])
97 }
98
99 pub fn edges_to(&self, idx: usize) -> impl Iterator<Item = &Edge> {
100 self.incoming[idx].iter().map(|&e| &self.edges[e])
101 }
102}