openquiver_graph/
document.rs1use std::collections::BTreeSet;
4
5use crate::{Anchor, Edge, Graph, Layout, Node, NodeName, PinRef, Trivia};
6
7#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
9pub enum RenameError {
10 #[error("there is no node named `{0}`")]
11 NoSuchNode(String),
12 #[error("a node named `{0}` already exists")]
13 NameTaken(String),
14 #[error("`{0}` is not a name the format can write")]
15 NotAnIdentifier(String),
16}
17
18#[derive(Debug, Clone, PartialEq)]
30pub struct Document {
31 pub graph: Graph,
32 pub trivia: Trivia,
33 pub layout: Layout,
34}
35
36impl Document {
37 pub fn new(graph: Graph, trivia: Trivia, layout: Layout) -> Self {
38 Self {
39 graph,
40 trivia,
41 layout,
42 }
43 }
44
45 pub fn remove_node(&mut self, name: &str) -> Option<Node> {
52 let key = NodeName::new(name);
53 let node = self.graph.nodes.remove(&key)?;
54
55 let name = key.as_str();
59 let touching: Vec<Edge> = self
60 .graph
61 .edges
62 .iter()
63 .filter(|e| e.from().node() == name || e.to().node() == name)
64 .cloned()
65 .collect();
66 for edge in touching {
67 self.graph.edges.remove(&edge);
68 self.trivia.0.remove(&Anchor::Edge(edge));
69 }
70
71 self.trivia
72 .0
73 .retain(|anchor, _| anchor.node() != Some(&key));
74 self.layout.0.remove(&key);
75
76 Some(node)
77 }
78
79 pub fn remove_edge(&mut self, edge: &Edge) -> bool {
81 self.trivia.0.remove(&Anchor::Edge(edge.clone()));
82 self.graph.edges.remove(edge)
83 }
84
85 pub fn rename_node(&mut self, from: &str, to: &str) -> Result<(), RenameError> {
92 let old = NodeName::new(from);
93 let new = NodeName::new(to);
94
95 if !self.graph.nodes.contains_key(&old) {
96 return Err(RenameError::NoSuchNode(from.to_owned()));
97 }
98 if !is_identifier(new.as_str()) {
101 return Err(RenameError::NotAnIdentifier(to.to_owned()));
102 }
103 if old != new && self.graph.nodes.contains_key(&new) {
106 return Err(RenameError::NameTaken(to.to_owned()));
107 }
108 if old == new {
109 return Ok(());
110 }
111
112 let mut node = self.graph.nodes.remove(&old).expect("checked above");
113 node.name = new.clone();
114 self.graph.nodes.insert(new.clone(), node);
115
116 let rewrite = |pin: &PinRef| -> PinRef {
117 if pin.node == old {
118 PinRef::new(new.clone(), pin.pin.clone())
119 } else {
120 pin.clone()
121 }
122 };
123
124 let mut moved = BTreeSet::new();
125 for edge in std::mem::take(&mut self.graph.edges) {
126 let next = Edge::new(edge.kind, rewrite(edge.from()), rewrite(edge.to()));
127 if let Some(comments) = self.trivia.0.remove(&Anchor::Edge(edge)) {
130 self.trivia
131 .0
132 .entry(Anchor::Edge(next.clone()))
133 .or_default()
134 .extend(comments);
135 }
136 moved.insert(next);
137 }
138 self.graph.edges = moved;
139
140 let rekeyed: Vec<(Anchor, Vec<String>)> = self
141 .trivia
142 .0
143 .extract_if(.., |anchor, _| anchor.node() == Some(&old))
144 .map(|(anchor, comments)| (anchor.with_node(new.clone()), comments))
145 .collect();
146 for (anchor, comments) in rekeyed {
147 self.trivia.0.entry(anchor).or_default().extend(comments);
148 }
149
150 if let Some(at) = self.layout.0.remove(&old) {
151 self.layout.0.insert(new, at);
152 }
153
154 Ok(())
155 }
156}
157
158pub fn is_identifier(name: &str) -> bool {
162 let mut chars = name.chars();
163 let Some(first) = chars.next() else {
164 return false;
165 };
166 (first == '_' || unicode_ident::is_xid_start(first))
167 && chars.all(unicode_ident::is_xid_continue)
168}