use std::cell::RefCell;
use std::rc::{Rc, Weak};
use graphrefly_core::Core;
use crate::graph::{destroy_subtree, fire_ns, 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 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,
}
}
}
fn new_child_inner(name: String, parent: Weak<RefCell<GraphInner>>) -> Rc<RefCell<GraphInner>> {
Rc::new(RefCell::new(GraphInner {
name,
names: indexmap::IndexMap::new(),
names_inverse: indexmap::IndexMap::new(),
children: indexmap::IndexMap::new(),
parent: Some(parent),
destroyed: false,
namespace_sinks: indexmap::IndexMap::new(),
next_ns_sink_id: 0,
}))
}
pub(crate) fn mount(
core: &Core,
parent_inner: &Rc<RefCell<GraphInner>>,
name: String,
child: &Graph,
) -> Result<Graph, MountError> {
if name.contains("::") {
return Err(MountError::InvalidName(name));
}
let child_inner = child.inner_arc().clone();
{
let mut p = parent_inner.borrow_mut();
if p.destroyed {
return Err(MountError::Destroyed);
}
if p.children.contains_key(&name) {
return Err(MountError::NameCollision(name));
}
if p.names.contains_key(&name) {
return Err(MountError::NodeNameCollision(name));
}
{
let mut c = child_inner.borrow_mut();
if c.parent.is_some() {
return Err(MountError::AlreadyMounted);
}
c.parent = Some(Rc::downgrade(parent_inner));
}
p.children.insert(name, child_inner.clone());
}
fire_ns(core, parent_inner);
Ok(Graph::from_inner(child_inner))
}
pub(crate) fn mount_new(
core: &Core,
parent_inner: &Rc<RefCell<GraphInner>>,
name: String,
) -> Result<Graph, MountError> {
if name.contains("::") {
return Err(MountError::InvalidName(name));
}
let parent_weak = Rc::downgrade(parent_inner);
let child_inner = {
let mut p = parent_inner.borrow_mut();
if p.destroyed {
return Err(MountError::Destroyed);
}
if p.children.contains_key(&name) {
return Err(MountError::NameCollision(name));
}
if p.names.contains_key(&name) {
return Err(MountError::NodeNameCollision(name));
}
let child_inner = new_child_inner(name.clone(), parent_weak);
p.children.insert(name, child_inner.clone());
child_inner
};
fire_ns(core, parent_inner);
Ok(Graph::from_inner(child_inner))
}
pub(crate) fn unmount(
core: &Core,
parent_inner: &Rc<RefCell<GraphInner>>,
name: &str,
) -> Result<GraphRemoveAudit, MountError> {
let child = {
let mut p = parent_inner.borrow_mut();
if p.destroyed {
return Err(MountError::Destroyed);
}
p.children
.shift_remove(name)
.ok_or_else(|| MountError::NotMounted(name.to_owned()))?
};
let audit = audit_of(&child);
child.borrow_mut().parent = None;
destroy_subtree(core, &child);
fire_ns(core, parent_inner);
Ok(audit)
}
pub(crate) fn ancestors(inner: &Rc<RefCell<GraphInner>>, include_self: bool) -> Vec<Graph> {
let mut chain: Vec<Graph> = Vec::new();
if include_self {
chain.push(Graph::from_inner(inner.clone()));
}
let mut visited = std::collections::HashSet::new();
visited.insert(Rc::as_ptr(inner) as usize);
let mut cursor: Option<Rc<RefCell<GraphInner>>> =
inner.borrow_mut().parent.as_ref().and_then(Weak::upgrade);
while let Some(cur) = cursor {
let ptr = Rc::as_ptr(&cur) as usize;
if !visited.insert(ptr) {
break; }
let next_parent = cur.borrow_mut().parent.as_ref().and_then(Weak::upgrade);
chain.push(Graph::from_inner(cur));
cursor = next_parent;
}
chain
}
fn audit_of(inner_arc: &Rc<RefCell<GraphInner>>) -> GraphRemoveAudit {
let inner = inner_arc.borrow_mut();
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<Rc<RefCell<GraphInner>>> = 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,
}
}