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)]
38pub struct GraphRemoveAudit {
39 pub node_count: usize,
41 pub mount_count: usize,
43}
44
45impl From<NameError> for MountError {
46 fn from(err: NameError) -> Self {
47 match err {
48 NameError::Collision(n) => Self::NodeNameCollision(n),
49 NameError::InvalidName(n) | NameError::ReservedPrefix(n) => Self::InvalidName(n),
50 NameError::Destroyed => Self::Destroyed,
51 }
52 }
53}
54
55fn new_child_inner(name: String, parent: Weak<RefCell<GraphInner>>) -> Rc<RefCell<GraphInner>> {
57 Rc::new(RefCell::new(GraphInner {
58 name,
59 names: indexmap::IndexMap::new(),
60 names_inverse: indexmap::IndexMap::new(),
61 children: indexmap::IndexMap::new(),
62 parent: Some(parent),
63 destroyed: false,
64 namespace_sinks: indexmap::IndexMap::new(),
65 next_ns_sink_id: 0,
66 }))
67}
68
69pub(crate) fn mount(
70 core: &Core,
71 parent_inner: &Rc<RefCell<GraphInner>>,
72 name: String,
73 child: &Graph,
74) -> Result<Graph, MountError> {
75 if name.contains("::") {
76 return Err(MountError::InvalidName(name));
77 }
78 let child_inner = child.inner_arc().clone();
79 {
83 let mut p = parent_inner.borrow_mut();
84 if p.destroyed {
85 return Err(MountError::Destroyed);
86 }
87 if p.children.contains_key(&name) {
88 return Err(MountError::NameCollision(name));
89 }
90 if p.names.contains_key(&name) {
91 return Err(MountError::NodeNameCollision(name));
92 }
93 {
94 let mut c = child_inner.borrow_mut();
95 if c.parent.is_some() {
96 return Err(MountError::AlreadyMounted);
97 }
98 c.parent = Some(Rc::downgrade(parent_inner));
99 }
100 p.children.insert(name, child_inner.clone());
101 }
102 fire_ns(core, parent_inner);
106 Ok(Graph::from_inner(child_inner))
107}
108
109pub(crate) fn mount_new(
110 core: &Core,
111 parent_inner: &Rc<RefCell<GraphInner>>,
112 name: String,
113) -> Result<Graph, MountError> {
114 if name.contains("::") {
115 return Err(MountError::InvalidName(name));
116 }
117 let parent_weak = Rc::downgrade(parent_inner);
118 let child_inner = {
121 let mut p = parent_inner.borrow_mut();
122 if p.destroyed {
123 return Err(MountError::Destroyed);
124 }
125 if p.children.contains_key(&name) {
126 return Err(MountError::NameCollision(name));
127 }
128 if p.names.contains_key(&name) {
129 return Err(MountError::NodeNameCollision(name));
130 }
131 let child_inner = new_child_inner(name.clone(), parent_weak);
132 p.children.insert(name, child_inner.clone());
133 child_inner
134 };
135 fire_ns(core, parent_inner);
137 Ok(Graph::from_inner(child_inner))
138}
139
140pub(crate) fn unmount(
141 core: &Core,
142 parent_inner: &Rc<RefCell<GraphInner>>,
143 name: &str,
144) -> Result<GraphRemoveAudit, MountError> {
145 let child = {
146 let mut p = parent_inner.borrow_mut();
147 if p.destroyed {
148 return Err(MountError::Destroyed);
149 }
150 p.children
151 .shift_remove(name)
152 .ok_or_else(|| MountError::NotMounted(name.to_owned()))?
153 };
154 let audit = audit_of(&child);
155 child.borrow_mut().parent = None;
157 destroy_subtree(core, &child);
158 fire_ns(core, parent_inner);
160 Ok(audit)
161}
162
163pub(crate) fn ancestors(inner: &Rc<RefCell<GraphInner>>, include_self: bool) -> Vec<Graph> {
164 let mut chain: Vec<Graph> = Vec::new();
165 if include_self {
166 chain.push(Graph::from_inner(inner.clone()));
167 }
168 let mut visited = std::collections::HashSet::new();
172 visited.insert(Rc::as_ptr(inner) as usize);
173 let mut cursor: Option<Rc<RefCell<GraphInner>>> =
174 inner.borrow_mut().parent.as_ref().and_then(Weak::upgrade);
175 while let Some(cur) = cursor {
176 let ptr = Rc::as_ptr(&cur) as usize;
177 if !visited.insert(ptr) {
178 break; }
180 let next_parent = cur.borrow_mut().parent.as_ref().and_then(Weak::upgrade);
181 chain.push(Graph::from_inner(cur));
182 cursor = next_parent;
183 }
184 chain
185}
186
187fn audit_of(inner_arc: &Rc<RefCell<GraphInner>>) -> GraphRemoveAudit {
188 let inner = inner_arc.borrow_mut();
189 let own = inner.names.len();
190 let mount_count_self = inner.children.len();
191 let mut node_count = own;
192 let mut mount_count = mount_count_self;
193 let kids: Vec<Rc<RefCell<GraphInner>>> = inner.children.values().cloned().collect();
194 drop(inner);
195 for kid in kids {
196 let sub = audit_of(&kid);
197 node_count += sub.node_count;
198 mount_count += sub.mount_count;
199 }
200 GraphRemoveAudit {
201 node_count,
202 mount_count,
203 }
204}