code_ranker_graph/
relativize.rs1use crate::level_graph::LevelGraph;
7use code_ranker_plugin_api::{attrs::AttrValue, edge::Edge, graph::Graph, node::Node};
8use std::cmp::Reverse;
9use std::collections::BTreeMap;
10use std::path::Path;
11
12pub fn relativize_level(level: &mut LevelGraph, target: &Path, roots: &BTreeMap<String, String>) {
19 let id_map = relativize_graph_inner(&mut level.nodes, &mut level.edges, target, roots);
20 for cycle in &mut level.cycles {
21 for n in &mut cycle.nodes {
22 if let Some(nn) = id_map.get(n) {
23 *n = nn.clone();
24 }
25 }
26 }
27}
28
29pub fn relativize_graph(graph: &mut Graph, target: &Path, roots: &BTreeMap<String, String>) {
34 relativize_graph_inner(&mut graph.nodes, &mut graph.edges, target, roots);
35}
36
37fn relativize_graph_inner(
38 nodes: &mut [Node],
39 edges: &mut [Edge],
40 target: &Path,
41 roots: &BTreeMap<String, String>,
42) -> BTreeMap<String, String> {
43 let mut id_map: BTreeMap<String, String> = BTreeMap::new();
44 for node in nodes.iter() {
45 if node.kind == "external" {
46 continue; }
48 let new_id = relativize_path(&node.id, target, roots);
49 if new_id != node.id {
50 id_map.insert(node.id.clone(), new_id);
51 }
52 }
53
54 for node in nodes.iter_mut() {
55 if let Some(new_id) = id_map.get(&node.id) {
56 node.id = new_id.clone();
57 }
58 if let Some(parent) = node.parent.as_mut()
59 && let Some(np) = id_map.get(parent)
60 {
61 *parent = np.clone();
62 }
63 if let Some(AttrValue::Str(p)) = node.attrs.get("path") {
64 let rel = relativize_path(p, target, roots);
65 if rel.is_empty() || rel == node.id {
66 node.attrs.remove("path");
67 } else {
68 node.attrs.insert("path".to_string(), AttrValue::Str(rel));
69 }
70 }
71 }
72 for edge in edges.iter_mut() {
73 if let Some(s) = id_map.get(&edge.source) {
74 edge.source = s.clone();
75 }
76 if let Some(t) = id_map.get(&edge.target) {
77 edge.target = t.clone();
78 }
79 }
80 id_map
81}
82
83fn relativize_path(path: &str, target: &Path, roots: &BTreeMap<String, String>) -> String {
84 if path.is_empty() {
85 return path.to_string();
86 }
87 let p = Path::new(path);
88 if let Ok(rel) = p.strip_prefix(target) {
89 return format!("{{target}}/{}", rel.to_string_lossy());
90 }
91 let mut sorted: Vec<_> = roots.iter().collect();
93 sorted.sort_by_key(|(_, root)| Reverse(root.len()));
94 for (name, root) in &sorted {
95 if let Ok(rel) = p.strip_prefix(root.as_str()) {
96 return format!("{{{name}}}/{}", rel.to_string_lossy());
97 }
98 }
99 path.to_string()
100}
101
102#[cfg(test)]
103#[path = "relativize_test.rs"]
104mod tests;