use std::sync::Arc;
use crate::graph::{Graph, GraphInner, NameError};
#[derive(Debug, thiserror::Error)]
pub enum MountError {
#[error("Graph::mount: name `{0}` already mounted in this graph")]
NameCollision(String),
#[error("Graph::mount: name `{0}` collides with an existing local node name")]
NodeNameCollision(String),
#[error("Graph::mount: name `{0}` may not contain the `::` path separator")]
InvalidName(String),
#[error(
"Graph::mount: child graph has a different Core (cross-Core mount is post-M6); \
clone-and-rebuild against this graph's Core, or use `mount_new` + builder"
)]
CoreMismatch,
#[error("Graph::mount: child graph already has a parent; unmount it first")]
AlreadyMounted,
#[error("Graph::unmount: no subgraph named `{0}`")]
NotMounted(String),
#[error("Graph::mount: graph has been destroyed")]
Destroyed,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GraphRemoveAudit {
pub node_count: usize,
pub mount_count: usize,
}
impl From<NameError> for MountError {
fn from(err: NameError) -> Self {
match err {
NameError::Collision(n) => Self::NodeNameCollision(n),
NameError::InvalidName(n) | NameError::ReservedPrefix(n) => Self::InvalidName(n),
NameError::Destroyed => Self::Destroyed,
}
}
}
pub(crate) fn mount(parent: &Graph, name: String, child: Graph) -> Result<Graph, MountError> {
if name.contains("::") {
return Err(MountError::InvalidName(name));
}
if !parent.core.same_dispatcher(&child.core) {
return Err(MountError::CoreMismatch);
}
{
let mut parent_inner = parent.inner.lock();
if parent_inner.destroyed {
return Err(MountError::Destroyed);
}
if parent_inner.children.contains_key(&name) {
return Err(MountError::NameCollision(name));
}
if parent_inner.names.contains_key(&name) {
return Err(MountError::NodeNameCollision(name));
}
{
let mut child_inner = child.inner.lock();
if child_inner.parent.is_some() {
return Err(MountError::AlreadyMounted);
}
child_inner.parent = Some(Arc::downgrade(&parent.inner));
}
parent_inner.children.insert(name, child.clone());
}
parent.fire_namespace_change();
Ok(child)
}
pub(crate) fn mount_new(parent: &Graph, name: String) -> Result<Graph, MountError> {
if name.contains("::") {
return Err(MountError::InvalidName(name));
}
let parent_weak = Arc::downgrade(&parent.inner);
let child = {
let mut parent_inner = parent.inner.lock();
if parent_inner.destroyed {
return Err(MountError::Destroyed);
}
if parent_inner.children.contains_key(&name) {
return Err(MountError::NameCollision(name));
}
if parent_inner.names.contains_key(&name) {
return Err(MountError::NodeNameCollision(name));
}
let child = Graph::with_core(name.clone(), parent.core.clone(), Some(parent_weak));
parent_inner.children.insert(name, child.clone());
child
};
parent.fire_namespace_change();
Ok(child)
}
pub(crate) fn unmount(parent: &Graph, name: &str) -> Result<GraphRemoveAudit, MountError> {
let child = {
let mut parent_inner = parent.inner.lock();
if parent_inner.destroyed {
return Err(MountError::Destroyed);
}
parent_inner
.children
.shift_remove(name)
.ok_or_else(|| MountError::NotMounted(name.to_owned()))?
};
let audit = audit_of(&child);
child.inner.lock().parent = None;
child.destroy();
parent.fire_namespace_change();
Ok(audit)
}
pub(crate) fn ancestors(graph: &Graph, include_self: bool) -> Vec<Graph> {
let mut chain: Vec<Graph> = Vec::new();
if include_self {
chain.push(graph.clone());
}
let mut visited = std::collections::HashSet::new();
visited.insert(Arc::as_ptr(&graph.inner) as usize);
let mut cursor: Option<Arc<parking_lot::Mutex<GraphInner>>> = graph
.inner
.lock()
.parent
.as_ref()
.and_then(std::sync::Weak::upgrade);
while let Some(inner) = cursor {
let ptr = Arc::as_ptr(&inner) as usize;
if !visited.insert(ptr) {
break; }
let next_parent = inner
.lock()
.parent
.as_ref()
.and_then(std::sync::Weak::upgrade);
chain.push(Graph {
core: graph.core.clone(),
inner,
});
cursor = next_parent;
}
chain
}
fn audit_of(graph: &Graph) -> GraphRemoveAudit {
let inner = graph.inner.lock();
let own = inner.names.len();
let mount_count_self = inner.children.len();
let mut node_count = own;
let mut mount_count = mount_count_self;
let kids: Vec<Graph> = inner.children.values().cloned().collect();
drop(inner);
for kid in kids {
let sub = audit_of(&kid);
node_count += sub.node_count;
mount_count += sub.mount_count;
}
GraphRemoveAudit {
node_count,
mount_count,
}
}