graphrefly_graph/
mount.rs1use std::cell::RefCell;
13use std::rc::{Rc, Weak};
14
15use graphrefly_core::Core;
16
17use crate::graph::{destroy_subtree, fire_ns, Graph, GraphInner, NameError};
18
19#[derive(Debug, thiserror::Error)]
21pub enum MountError {
22 #[error("Graph::mount: name `{0}` already mounted in this graph")]
23 NameCollision(String),
24 #[error("Graph::mount: name `{0}` collides with an existing local node name")]
25 NodeNameCollision(String),
26 #[error("Graph::mount: name `{0}` may not contain the `::` path separator")]
27 InvalidName(String),
28 #[error("Graph::mount: child graph already has a parent; unmount it first")]
29 AlreadyMounted,
30 #[error("Graph::unmount: no subgraph named `{0}`")]
31 NotMounted(String),
32 #[error("Graph::mount: graph has been destroyed")]
33 Destroyed,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct GraphRemoveAudit {
61 pub node_count: usize,
63 pub mount_count: usize,
65}
66
67impl From<NameError> for MountError {
68 fn from(err: NameError) -> Self {
69 match err {
70 NameError::Collision(n) => Self::NodeNameCollision(n),
71 NameError::InvalidName(n) => Self::InvalidName(n),
72 NameError::Destroyed => Self::Destroyed,
73 }
74 }
75}
76
77fn new_child_inner(name: String, parent: Weak<RefCell<GraphInner>>) -> Rc<RefCell<GraphInner>> {
79 Rc::new(RefCell::new(GraphInner {
80 name,
81 names: indexmap::IndexMap::new(),
82 names_inverse: indexmap::IndexMap::new(),
83 children: indexmap::IndexMap::new(),
84 parent: Some(parent),
85 destroyed: false,
86 namespace_sinks: indexmap::IndexMap::new(),
87 next_ns_sink_id: 0,
88 factory: None,
89 factory_args: None,
90 }))
91}
92
93pub(crate) fn mount(
94 core: &Core,
95 parent_inner: &Rc<RefCell<GraphInner>>,
96 name: String,
97 child: &Graph,
98) -> Result<Graph, MountError> {
99 if name.contains("::") {
100 return Err(MountError::InvalidName(name));
101 }
102 let child_inner = child.inner_arc().clone();
103 {
107 let mut p = parent_inner.borrow_mut();
108 if p.destroyed {
109 return Err(MountError::Destroyed);
110 }
111 if p.children.contains_key(&name) {
112 return Err(MountError::NameCollision(name));
113 }
114 if p.names.contains_key(&name) {
115 return Err(MountError::NodeNameCollision(name));
116 }
117 {
118 let mut c = child_inner.borrow_mut();
119 if c.parent.is_some() {
120 return Err(MountError::AlreadyMounted);
121 }
122 c.parent = Some(Rc::downgrade(parent_inner));
123 }
124 p.children.insert(name, child_inner.clone());
125 }
126 fire_ns(core, parent_inner);
130 Ok(Graph::from_inner(child_inner))
131}
132
133pub(crate) fn mount_new(
134 core: &Core,
135 parent_inner: &Rc<RefCell<GraphInner>>,
136 name: String,
137) -> Result<Graph, MountError> {
138 if name.contains("::") {
139 return Err(MountError::InvalidName(name));
140 }
141 let parent_weak = Rc::downgrade(parent_inner);
142 let child_inner = {
145 let mut p = parent_inner.borrow_mut();
146 if p.destroyed {
147 return Err(MountError::Destroyed);
148 }
149 if p.children.contains_key(&name) {
150 return Err(MountError::NameCollision(name));
151 }
152 if p.names.contains_key(&name) {
153 return Err(MountError::NodeNameCollision(name));
154 }
155 let child_inner = new_child_inner(name.clone(), parent_weak);
156 p.children.insert(name, child_inner.clone());
157 child_inner
158 };
159 fire_ns(core, parent_inner);
161 Ok(Graph::from_inner(child_inner))
162}
163
164pub(crate) fn unmount(
165 core: &Core,
166 parent_inner: &Rc<RefCell<GraphInner>>,
167 name: &str,
168) -> Result<GraphRemoveAudit, MountError> {
169 let child = {
170 let mut p = parent_inner.borrow_mut();
171 if p.destroyed {
172 return Err(MountError::Destroyed);
173 }
174 p.children
175 .shift_remove(name)
176 .ok_or_else(|| MountError::NotMounted(name.to_owned()))?
177 };
178 let audit = audit_of(&child);
179 child.borrow_mut().parent = None;
181 destroy_subtree(core, &child);
182 fire_ns(core, parent_inner);
184 Ok(audit)
185}
186
187pub(crate) fn ancestors(inner: &Rc<RefCell<GraphInner>>, include_self: bool) -> Vec<Graph> {
188 let mut chain: Vec<Graph> = Vec::new();
189 if include_self {
190 chain.push(Graph::from_inner(inner.clone()));
191 }
192 let mut visited = std::collections::HashSet::new();
196 visited.insert(Rc::as_ptr(inner) as usize);
197 let mut cursor: Option<Rc<RefCell<GraphInner>>> =
198 inner.borrow_mut().parent.as_ref().and_then(Weak::upgrade);
199 while let Some(cur) = cursor {
200 let ptr = Rc::as_ptr(&cur) as usize;
201 if !visited.insert(ptr) {
202 break; }
204 let next_parent = cur.borrow_mut().parent.as_ref().and_then(Weak::upgrade);
205 chain.push(Graph::from_inner(cur));
206 cursor = next_parent;
207 }
208 chain
209}
210
211fn audit_of(inner_arc: &Rc<RefCell<GraphInner>>) -> GraphRemoveAudit {
218 let inner = inner_arc.borrow_mut();
219 let own = inner.names.len();
220 let mount_count_self = inner.children.len();
221 let mut node_count = own;
222 let mut mount_count = mount_count_self;
223 let kids: Vec<Rc<RefCell<GraphInner>>> = inner.children.values().cloned().collect();
224 drop(inner);
225 for kid in kids {
226 let sub = audit_of(&kid);
227 node_count += sub.node_count;
228 mount_count += sub.mount_count;
229 }
230 GraphRemoveAudit {
231 node_count,
232 mount_count,
233 }
234}