///|
fn rename_node_graph(graph : Graph, old_id : String, new_id : String) -> Graph {
{
..graph,
nodes: graph.nodes.map(fn(node) {
{
..node,
id: if node.id == old_id {
new_id
} else {
node.id
},
graph: node.graph.map(fn(nested) {
rename_node_graph(nested, old_id, new_id)
}),
}
}),
edges: graph.edges.map(fn(edge) {
{
..edge,
source: if edge.source == old_id {
new_id
} else {
edge.source
},
target: if edge.target == old_id {
new_id
} else {
edge.target
},
graph: edge.graph.map(fn(nested) {
rename_node_graph(nested, old_id, new_id)
}),
}
}),
hyperedges: graph.hyperedges.map(fn(hyperedge) {
{
..hyperedge,
endpoints: hyperedge.endpoints.map(fn(endpoint) {
{
..endpoint,
node: if endpoint.node == old_id {
new_id
} else {
endpoint.node
},
}
}),
graph: hyperedge.graph.map(fn(nested) {
rename_node_graph(nested, old_id, new_id)
}),
}
}),
}
}
///|
/// Rename one node in a top-level ID scope and update all edge/hyperedge references.
pub fn Document::rename_node(
self : Document,
root : Int32,
old_id : String,
new_id : String,
limits~ : Limits = Limits::default(),
) -> Document raise GraphError {
bounded_index(root, self.graphs.length(), "rename.root")
valid_token(new_id, "node.id")
let index = GraphIndex::new(self)
require(
index.node_address(root, old_id) is Some(_),
"node.id",
"node to rename does not exist",
)
require(
old_id == new_id || index.node_address(root, new_id) is None,
"node.id",
"new node identifier already exists",
)
self.replace_graph(
GraphAddress::root(root),
rename_node_graph(self.graphs[root], old_id, new_id),
limits~,
)
}
///|
/// Replace a node without deleting any incident references implicitly.
pub fn Document::replace_node(
self : Document,
address : ElementAddress,
node : Node,
limits~ : Limits = Limits::default(),
) -> Document raise GraphError {
let graph = graph_at(self, address.graph)
bounded_index(address.index, graph.nodes.length(), "replace.node")
let nodes = graph.nodes.copy()
nodes[address.index] = node
self.replace_graph(address.graph, { ..graph, nodes, }, limits~)
}
///|
/// Replace edge properties, preserving hierarchy and enforcing reference validity.
pub fn Document::replace_edge(
self : Document,
address : ElementAddress,
edge : Edge,
limits~ : Limits = Limits::default(),
) -> Document raise GraphError {
let graph = graph_at(self, address.graph)
bounded_index(address.index, graph.edges.length(), "replace.edge")
let edges = graph.edges.copy()
edges[address.index] = edge
self.replace_graph(address.graph, { ..graph, edges, }, limits~)
}