graphrefly_graph/
mount.rs1use std::sync::Arc;
9
10use crate::graph::{Graph, GraphInner, NameError};
11
12#[derive(Debug, thiserror::Error)]
14pub enum MountError {
15 #[error("Graph::mount: name `{0}` already mounted in this graph")]
16 NameCollision(String),
17 #[error("Graph::mount: name `{0}` collides with an existing local node name")]
18 NodeNameCollision(String),
19 #[error("Graph::mount: name `{0}` may not contain the `::` path separator")]
20 InvalidName(String),
21 #[error(
22 "Graph::mount: child graph has a different Core (cross-Core mount is post-M6); \
23 clone-and-rebuild against this graph's Core, or use `mount_new` + builder"
24 )]
25 CoreMismatch,
26 #[error("Graph::mount: child graph already has a parent; unmount it first")]
27 AlreadyMounted,
28 #[error("Graph::unmount: no subgraph named `{0}`")]
29 NotMounted(String),
30 #[error("Graph::mount: graph has been destroyed")]
31 Destroyed,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct GraphRemoveAudit {
38 pub node_count: usize,
40 pub mount_count: usize,
42}
43
44impl From<NameError> for MountError {
45 fn from(err: NameError) -> Self {
46 match err {
47 NameError::Collision(n) => Self::NodeNameCollision(n),
48 NameError::InvalidName(n) | NameError::ReservedPrefix(n) => Self::InvalidName(n),
49 NameError::Destroyed => Self::Destroyed,
50 }
51 }
52}
53
54pub(crate) fn mount(parent: &Graph, name: String, child: Graph) -> Result<Graph, MountError> {
55 if name.contains("::") {
56 return Err(MountError::InvalidName(name));
57 }
58 if !parent.core.same_dispatcher(&child.core) {
61 return Err(MountError::CoreMismatch);
62 }
63 {
71 let mut parent_inner = parent.inner.lock();
72 if parent_inner.destroyed {
73 return Err(MountError::Destroyed);
74 }
75 if parent_inner.children.contains_key(&name) {
76 return Err(MountError::NameCollision(name));
77 }
78 if parent_inner.names.contains_key(&name) {
79 return Err(MountError::NodeNameCollision(name));
80 }
81 {
82 let mut child_inner = child.inner.lock();
83 if child_inner.parent.is_some() {
84 return Err(MountError::AlreadyMounted);
85 }
86 child_inner.parent = Some(Arc::downgrade(&parent.inner));
87 }
88 parent_inner.children.insert(name, child.clone());
89 }
90 parent.fire_namespace_change();
93 Ok(child)
94}
95
96pub(crate) fn mount_new(parent: &Graph, name: String) -> Result<Graph, MountError> {
97 if name.contains("::") {
98 return Err(MountError::InvalidName(name));
99 }
100 let parent_weak = Arc::downgrade(&parent.inner);
101 let child = {
106 let mut parent_inner = parent.inner.lock();
107 if parent_inner.destroyed {
108 return Err(MountError::Destroyed);
109 }
110 if parent_inner.children.contains_key(&name) {
111 return Err(MountError::NameCollision(name));
112 }
113 if parent_inner.names.contains_key(&name) {
114 return Err(MountError::NodeNameCollision(name));
115 }
116 let child = Graph::with_core(name.clone(), parent.core.clone(), Some(parent_weak));
117 parent_inner.children.insert(name, child.clone());
118 child
119 };
120 parent.fire_namespace_change();
122 Ok(child)
123}
124
125pub(crate) fn unmount(parent: &Graph, name: &str) -> Result<GraphRemoveAudit, MountError> {
126 let child = {
127 let mut parent_inner = parent.inner.lock();
128 if parent_inner.destroyed {
129 return Err(MountError::Destroyed);
130 }
131 parent_inner
132 .children
133 .shift_remove(name)
134 .ok_or_else(|| MountError::NotMounted(name.to_owned()))?
135 };
136 let audit = audit_of(&child);
137 child.inner.lock().parent = None;
139 child.destroy();
140 parent.fire_namespace_change();
142 Ok(audit)
143}
144
145pub(crate) fn ancestors(graph: &Graph, include_self: bool) -> Vec<Graph> {
146 let mut chain: Vec<Graph> = Vec::new();
147 if include_self {
148 chain.push(graph.clone());
149 }
150 let mut visited = std::collections::HashSet::new();
159 visited.insert(Arc::as_ptr(&graph.inner) as usize);
160 let mut cursor: Option<Arc<parking_lot::Mutex<GraphInner>>> = graph
161 .inner
162 .lock()
163 .parent
164 .as_ref()
165 .and_then(std::sync::Weak::upgrade);
166 while let Some(inner) = cursor {
167 let ptr = Arc::as_ptr(&inner) as usize;
168 if !visited.insert(ptr) {
169 break; }
171 let next_parent = inner
172 .lock()
173 .parent
174 .as_ref()
175 .and_then(std::sync::Weak::upgrade);
176 chain.push(Graph {
177 core: graph.core.clone(),
178 inner,
179 });
180 cursor = next_parent;
181 }
182 chain
183}
184
185fn audit_of(graph: &Graph) -> GraphRemoveAudit {
186 let inner = graph.inner.lock();
187 let own = inner.names.len();
188 let mount_count_self = inner.children.len();
189 let mut node_count = own;
190 let mut mount_count = mount_count_self;
191 let kids: Vec<Graph> = inner.children.values().cloned().collect();
192 drop(inner);
193 for kid in kids {
194 let sub = audit_of(&kid);
195 node_count += sub.node_count;
196 mount_count += sub.mount_count;
197 }
198 GraphRemoveAudit {
199 node_count,
200 mount_count,
201 }
202}