13 Commits

Author SHA1 Message Date
warrence 8f9f0bf4c8 Add language tag to readme example code for syntax highlighting 2026-07-17 11:21:31 +02:00
warrence 7345684f00 Add documentation for traits module 2026-07-17 11:04:08 +02:00
warrence 2c53d92943 Fix readme typo 2026-07-17 11:00:02 +02:00
warrence c131f520f4 Add crate doc front-page, and update readme 2026-07-14 20:01:08 +02:00
warrence b6c27ea605 Merge branch 'main' into docs 2026-07-14 09:54:17 +02:00
warrence 5b30151010 Merge pull request 'Minor fixes' (#5) from fixes-minor into main
Reviewed-on: #5
2026-07-13 10:58:50 +02:00
warrence 2f549cc506 Update package version 2026-07-13 10:49:25 +02:00
warrence cf31ee2f01 Replace VertexMap/EdgeMap.len with capacity
Rename EdgeMap.len to capacity.
Add VertexMap.capacity and EdgeMap.capacity.
Deprecate VertexMap.len and EdgeMap.len.
2026-07-13 10:48:16 +02:00
warrence e58261ba47 Add BfsImplResult and DfsImplResult as named return structs 2026-07-10 13:12:30 +02:00
warrence 818b37ec15 Add linter warning for missing docs 2026-07-10 10:18:39 +02:00
warrence dd0399dcdd Merge pull request 'Add prelude' (#4) from feature/prelude into main
Reviewed-on: #4
2026-07-07 11:45:41 +02:00
warrence 3c4391ac1d Update package version 2026-07-07 11:44:57 +02:00
warrence bbd9506bbc Add prelude module for common traits 2026-07-07 11:44:17 +02:00
8 changed files with 466 additions and 41 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "grapherity"
version = "0.2.0"
version = "0.2.2"
authors = ["Stefan Müller"]
edition = "2024"
rust-version = "1.85.0"
+58 -3
View File
@@ -1,14 +1,69 @@
# grapherity
Library for graph models and algorithms.
Graph models and algorithms.
This library is still experimental and its API may therefore change frequently.
This crate provides data structures to model graphs and to perform algorithms on these data structures. The functionality presented is designed to be easy to use, flexible, and performant.
Currently supported are:
* Undirected graph types `Graph` and `AppendGraph`, built on a flat, index-based adjacency list,
* Maps to freely associate custom data with vertices and edges,
* Connectivity and pathing algorithms: Dijkstra's algorithm, DFS, and BFS in different variants,
* Exposed traits to implement custom graph data structures or algorithms.
For more information, see the [documentation](https://docs.rs/grapherity/latest/grapherity/).
## Example
```rust
// Brings commonly used traits into scope.
use grapherity::prelude::*;
use grapherity::models::Graph;
use grapherity::algorithms;
let mut graph = Graph::new();
// Creates a map for edge weights with a default weight.
let mut weights = graph.edge_map(1);
// Adds three vertices and two edges.
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let v3 = graph.add_vertex();
graph.add_edge(v1, v2);
let e = graph.add_edge(v2, v3);
// Sets a non-default weight for the second edge.
weights[e] = 5;
// Checks the number of vertices.
assert_eq!(graph.vertex_count(), 3);
// Checks the number of edges.
assert_eq!(graph.edge_count(), 2);
// Checks that v1 and v2 are adjacent.
assert!(graph.are_adjacent(v1, v2));
// Checks that v1 and v3 are not adjacent.
assert!(!graph.are_adjacent(v1, v3));
// Checks the sum of all vertex degrees.
let sum: usize = graph.vertices().map(|v| graph.degree(v)).sum();
assert_eq!(sum, 1 + 2 + 1);
// Calls Dijkstra's algorithm using the edge weights to find the shortest path from v1 to v3.
let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
assert_eq!(result.distances[v3], Some(1 + 5));
assert_eq!(result.predecessors[v3], Some(v2));
// Deletes the middle vertex and its incident edges.
graph.delete_vertex(v2);
// Calls Dijkstra's algorithm again on the now disconnected graph.
let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
assert_eq!(result.distances[v3], None);
assert_eq!(result.predecessors[v3], None);
```
## License
This project is dual-licensed under the terms of both:
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/mit)
You may choose either license for your use of this software.
+36 -22
View File
@@ -122,12 +122,12 @@ where
G: GraphTopology,
{
let mut predecessors = graph.vertex_map(None);
let (distances, _) = bfs_impl(graph, source, |neighbor, predecessor| {
let result = bfs_impl(graph, source, |neighbor, predecessor| {
predecessors[neighbor] = Some(predecessor);
true
});
BfsResult {
distances,
distances: result.distances,
predecessors,
}
}
@@ -136,7 +136,7 @@ pub fn bfs_distances<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, Op
where
G: GraphTopology,
{
bfs_impl(graph, source, |_, _| true).0
bfs_impl(graph, source, |_, _| true).distances
}
pub fn bfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<u32>
@@ -154,14 +154,15 @@ where
if predicate(source) {
return Some((source, 0));
}
bfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).1
bfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).found
}
fn bfs_impl<G, F>(
graph: &G,
source: G::Vertex,
mut on_discover: F,
) -> (VertexMap<G::Vertex, Option<u32>>, Option<(G::Vertex, u32)>)
struct BfsImplResult<V: Copy> {
distances: VertexMap<V, Option<u32>>,
found: Option<(V, u32)>,
}
fn bfs_impl<G, F>(graph: &G, source: G::Vertex, mut on_discover: F) -> BfsImplResult<G::Vertex>
where
G: GraphTopology,
F: FnMut(G::Vertex, G::Vertex) -> bool,
@@ -178,13 +179,19 @@ where
let distance = distances[v].unwrap() + 1;
distances[neighbor] = Some(distance);
if !on_discover(neighbor, v) {
return (distances, Some((neighbor, distance)));
return BfsImplResult {
distances,
found: Some((neighbor, distance)),
};
}
queue.push_back(neighbor);
}
}
}
(distances, None)
BfsImplResult {
distances,
found: None,
}
}
pub struct DfsResult<V: Copy> {
@@ -197,12 +204,12 @@ where
G: GraphTopology,
{
let mut predecessors = graph.vertex_map(None);
let (visited, _) = dfs_impl(graph, source, |neighbor, predecessor| {
let result = dfs_impl(graph, source, |neighbor, predecessor| {
predecessors[neighbor] = Some(predecessor);
true
});
DfsResult {
visited,
visited: result.visited,
predecessors,
}
}
@@ -211,7 +218,7 @@ pub fn dfs_visited<G>(graph: &G, source: G::Vertex) -> VertexMap<G::Vertex, bool
where
G: GraphTopology,
{
dfs_impl(graph, source, |_, _| true).0
dfs_impl(graph, source, |_, _| true).visited
}
pub fn dfs_find<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> bool
@@ -229,14 +236,15 @@ where
if predicate(source) {
return Some(source);
}
dfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).1
dfs_impl(graph, source, |neighbor, _| !predicate(neighbor)).found
}
fn dfs_impl<G, F>(
graph: &G,
source: G::Vertex,
mut on_discover: F,
) -> (VertexMap<G::Vertex, bool>, Option<G::Vertex>)
struct DfsImplResult<V: Copy> {
visited: VertexMap<V, bool>,
found: Option<V>,
}
fn dfs_impl<G, F>(graph: &G, source: G::Vertex, mut on_discover: F) -> DfsImplResult<G::Vertex>
where
G: GraphTopology,
F: FnMut(G::Vertex, G::Vertex) -> bool,
@@ -248,7 +256,10 @@ where
while let Some((v, predecessor)) = stack.pop() {
if let Some(p) = predecessor {
if !on_discover(v, p) {
return (visited, Some(v));
return DfsImplResult {
visited,
found: Some(v),
};
}
}
for neighbor in graph.adjacent_vertices(v) {
@@ -258,7 +269,10 @@ where
}
}
}
(visited, None)
DfsImplResult {
visited,
found: None,
}
}
pub fn dfs_find_path<G>(graph: &G, source: G::Vertex, target: G::Vertex) -> Option<Vec<G::Edge>>
+135
View File
@@ -1,6 +1,141 @@
//! Graph models and algorithms.
//!
//! This crate provides data structures to model graphs and to perform algorithms on these data
//! structures. The functionality presented is designed to be easy to use, flexible, and performant.
//!
//! Currently supported are:
//!
//! * Undirected graph types [`Graph`] and [`AppendGraph`] built on a flat, index-based adjacency
//! list,
//! * [`VertexMap`] and [`EdgeMap`] to freely associate custom data with vertices and edges,
//! * Connectivity and pathing algorithms: [Dijkstra's algorithm], [DFS], and [BFS] in different
//! variants,
//! * Exposed traits to implement custom graph data structures or algorithms.
//!
//! # Usage example
//!
//! This example demonstrates some of this library's features.
//!
//! ```
//! // Brings commonly used traits into scope.
//! use grapherity::prelude::*;
//! use grapherity::models::Graph;
//! use grapherity::algorithms;
//!
//! let mut graph = Graph::new();
//! // Creates a map for edge weights with a default weight.
//! let mut weights = graph.edge_map(1);
//! // Adds three vertices and two edges.
//! let v1 = graph.add_vertex();
//! let v2 = graph.add_vertex();
//! let v3 = graph.add_vertex();
//! graph.add_edge(v1, v2);
//! let e = graph.add_edge(v2, v3);
//! // Sets a non-default weight for the second edge.
//! weights[e] = 5;
//!
//! // Checks the number of vertices.
//! assert_eq!(graph.vertex_count(), 3);
//! // Checks the number of edges.
//! assert_eq!(graph.edge_count(), 2);
//! // Checks that v1 and v2 are adjacent.
//! assert!(graph.are_adjacent(v1, v2));
//! // Checks that v1 and v3 are not adjacent.
//! assert!(!graph.are_adjacent(v1, v3));
//! // Checks the sum of all vertex degrees.
//! let sum: usize = graph.vertices().map(|v| graph.degree(v)).sum();
//! assert_eq!(sum, 1 + 2 + 1);
//!
//! // Calls Dijkstra's algorithm using the edge weights to find the shortest path from v1 to v3.
//! let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
//! assert_eq!(result.distances[v3], Some(1 + 5));
//! assert_eq!(result.predecessors[v3], Some(v2));
//!
//! // Deletes the middle vertex and its incident edges.
//! graph.delete_vertex(v2);
//!
//! // Calls Dijkstra's algorithm again on the now disconnected graph.
//! let result = algorithms::dijkstra(&graph, v1, |e| weights[e]);
//! assert_eq!(result.distances[v3], None);
//! assert_eq!(result.predecessors[v3], None);
//! ```
//!
//! # Graph types
//!
//! [`Graph`] and [`AppendGraph`] both implement [`GraphTopology`] using a flat, index-based adjacency
//! list to store edges, which means that vertex and edge insertions happen in `O(1)`. Degree
//! lookups are also `O(1)`.
//!
//! [`Graph`] additionally implements [`GraphTopologyDeletion`], thereby supporting deletion of
//! vertices and edges.
//!
//! [`AppendGraph`] does not support deletion, vertices and edges can only be added, allowing it to
//! be smaller and more performant compared to [`Graph`] by not requiring the per-element generation
//! tracking needed for stable handles after deletion.
//!
//! # Vertices and edges
//!
//! Graphs in this library use stable indices [`GraphTopology::Vertex`] and [`GraphTopology::Edge`]
//! to denote vertices and edges. This means that vertices and edges returned by graph functions
//! never change, even if the underlying graph data structure is mutated. However, vertices and
//! edges can become invalid when the denoted element is deleted. Code using an invalid vertex or
//! edge in a graph method will panic.
//!
//! ```should_panic
//! # use grapherity::prelude::*;
//! # use grapherity::models::Graph;
//! let mut graph = Graph::new();
//! let v = graph.add_vertex();
//! graph.delete_vertex(v);
//! // This will panic because 'v' was deleted and is not valid anymore.
//! graph.degree(v);
//! ```
//!
//! [`VertexMap`] and [`EdgeMap`] are not required to panic if provided with an invalid
//! vertex or edge.
//!
//! Vertices and edges are provided by the graph and not intended to be generated by users.
//!
//! # Loops and multi-edges
//!
//! Loops and multi edges are supported as well. In the following example, `v1` will have two loops
//! and two edges to `v2`.
//!
//! ```
//! # use grapherity::prelude::*;
//! # use grapherity::models::Graph;
//! let mut graph = Graph::new();
//! let v1 = graph.add_vertex();
//! let v2 = graph.add_vertex();
//! for _ in 0..2 {
//! graph.add_edge(v1, v1);
//! graph.add_edge(v1, v2);
//! }
//! assert_eq!(graph.edge_count(), 4);
//! assert_eq!(graph.degree(v1), 6);
//! ```
//!
//! [BFS]: crate::algorithms::bfs
//! [Dijkstra's algorithm]: crate::algorithms::dijkstra
//! [DFS]: crate::algorithms::dfs
//! [`EdgeMap`]: crate::maps::EdgeMap
//! [`VertexMap`]: crate::maps::VertexMap
//! [`AppendGraph`]: crate::models::AppendGraph
//! [`Graph`]: crate::models::Graph
//! [`GraphTopology`]: crate::traits::GraphTopology
//! [`GraphTopology::Edge`]: crate::traits::GraphTopology::Edge
//! [`GraphTopology::Vertex`]: crate::traits::GraphTopology::Vertex
//! [`GraphTopologyDeletion`]: crate::traits::GraphTopologyDeletion
#![warn(missing_docs)]
pub mod algorithms;
pub mod maps;
pub mod models;
pub mod traits;
pub mod prelude {
pub use crate::traits::{GraphTopology, GraphTopologyDeletion};
}
mod testing;
+15 -5
View File
@@ -13,9 +13,14 @@ impl<V: Copy, T: Clone> VertexMap<V, T> {
}
}
// Reads beyond len() are valid and return the default value.
// Reads beyond 'capacity' are valid and return the default value.
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[deprecated(since = "0.2.2", note = "use 'capacity' instead")]
pub fn len(&self) -> usize {
self.inner.len()
self.capacity()
}
pub fn sync<G: GraphTopology<Vertex = V>>(&mut self, graph: &G) {
@@ -48,9 +53,14 @@ impl<E: Copy, T: Clone> EdgeMap<E, T> {
}
}
// Reads beyond len() are valid and return the default value.
// Reads beyond 'capacity' are valid and return the default value.
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[deprecated(since = "0.2.2", note = "use 'capacity' instead")]
pub fn len(&self) -> usize {
self.inner.len()
self.capacity()
}
pub fn sync<G: GraphTopology<Edge = E>>(&mut self, graph: &G) {
@@ -87,7 +97,7 @@ impl<E: Copy, T: Clone> EntityMap<E, T> {
}
}
pub fn len(&self) -> usize {
pub fn capacity(&self) -> usize {
self.data.len()
}
+2
View File
@@ -1,5 +1,7 @@
pub mod append_graph;
pub mod graph;
// TODO: Compressed-sparse-row graph model.
pub use append_graph::{AppendGraph, AppendGraphEdgeMap, AppendGraphVertexMap};
pub use graph::{Graph, GraphEdgeMap, GraphVertexMap};
+10 -10
View File
@@ -53,16 +53,16 @@ macro_rules! vertex_map_tests {
let mut graph = <$T>::new();
graph.add_vertex();
let mut map = graph.vertex_map(42);
let initial_len = map.len();
while graph.vertex_capacity() <= initial_len {
let capacity_before = map.capacity();
while graph.vertex_capacity() <= capacity_before {
graph.add_vertex();
}
assert!(
map.len() < graph.vertex_capacity(),
map.capacity() < graph.vertex_capacity(),
"precondition: map is stale before sync"
);
map.sync(&graph);
assert_eq!(map.len(), graph.vertex_capacity());
assert_eq!(map.capacity(), graph.vertex_capacity());
}
#[test]
@@ -109,7 +109,7 @@ macro_rules! vertex_map_deletion_tests {
let capacity_before = graph.vertex_capacity();
graph.delete_vertex(v2);
map.sync(&graph);
assert_eq!(map.len(), capacity_before);
assert_eq!(map.capacity(), capacity_before);
assert_eq!(map[v1], 5);
}
@@ -197,16 +197,16 @@ macro_rules! edge_map_tests {
let v2 = graph.add_vertex();
graph.add_edge(v1, v2);
let mut map = graph.edge_map(42);
let initial_len = map.len();
while graph.edge_capacity() <= initial_len {
let capacity_before = map.capacity();
while graph.edge_capacity() <= capacity_before {
graph.add_edge(v1, v2);
}
assert!(
map.len() < graph.edge_capacity(),
map.capacity() < graph.edge_capacity(),
"precondition: map is stale before sync"
);
map.sync(&graph);
assert_eq!(map.len(), graph.edge_capacity());
assert_eq!(map.capacity(), graph.edge_capacity());
}
#[test]
@@ -259,7 +259,7 @@ macro_rules! edge_map_deletion_tests {
let capacity_before = graph.edge_capacity();
graph.delete_edge(e2);
map.sync(&graph);
assert_eq!(map.len(), capacity_before);
assert_eq!(map.capacity(), capacity_before);
assert_eq!(map[e1], 5);
}
+209
View File
@@ -2,35 +2,244 @@ use crate::maps::{EdgeMap, VertexMap};
// TODO: Add functions to reserve memory for vertices and edges.
// TODO: Split out GraphTopologyAddition trait.
/// A trait representing an undirected graph topology.
///
/// An undirected graph is a set of vertices and undirected edges, where each edge connects either
/// exactly two vertices or one vertex with itself (loop edge). This trait provides methods for
/// querying a graph topology, iterating over vertices and edges, and adding new vertices and edges.
///
/// # Vertices and Edges
///
/// Vertices and edges are identified by opaque handles ([`Vertex`] and [`Edge`]) that implement
/// [`Copy`] and [`Eq`]. Handles remain valid for the lifetime of the graph unless the graph also
/// implements [`GraphTopologyDeletion`] and the item identified by the handle is explicitly
/// deleted.
///
/// Methods accepting vertices or edges as parameters panic if the handle was invalidated by a
/// deletion, and return incorrect results if the handle was not produced by this graph instance.
///
/// # Deletion
///
/// This trait covers graph construction and querying only. To delete vertices and edges, see
/// [`GraphTopologyDeletion`].
///
/// [`Edge`]: GraphTopology::Edge
/// [`Vertex`]: GraphTopology::Vertex
pub trait GraphTopology {
/// An opaque, stable handle identifying a vertex.
type Vertex: Copy + Eq;
/// An opaque, stable handle identifying an edge.
type Edge: Copy + Eq;
/// A resumable position in the incidence list of a vertex.
///
/// Prefer [`incidences`](Self::incidences) for straightforward iteration. A cursor is useful
/// when an algorithm needs to pause traversal, perform other graph queries or mutations, and
/// then continue from where it left off. This cursor type can be obtained via
/// [`incidence_cursor`](Self::incidence_cursor).
type IncidenceCursor: IncidenceCursor<Self> + Copy;
/// Returns the number of vertices in the graph.
fn vertex_count(&self) -> usize;
/// Returns the total number of vertices the graph can hold without reallocating.
fn vertex_capacity(&self) -> usize;
/// Creates and returns a [`VertexMap`] with every slot initialised to `default`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let v1 = graph.add_vertex();
/// let mut labels = graph.vertex_map("z");
/// assert_eq!(labels[v1], "z");
/// labels[v1] = "a";
/// assert_eq!(labels[v1], "a");
///
/// // A new vertex is immediately available for read and write.
/// let v2 = graph.add_vertex();
/// assert_eq!(labels[v2], "z");
/// labels[v2] = "b";
/// assert_eq!(labels[v2], "b");
/// ```
fn vertex_map<T: Clone>(&self, default: T) -> VertexMap<Self::Vertex, T>;
/// Returns the number of edges in the graph.
fn edge_count(&self) -> usize;
/// Returns the total number of edges the graph can hold without reallocating.
fn edge_capacity(&self) -> usize;
/// Creates and returns an [`EdgeMap`] with every slot initialised to `default`.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::models::Graph;
/// let mut graph = Graph::new();
/// let v1 = graph.add_vertex();
/// let v2 = graph.add_vertex();
/// let e1 = graph.add_edge(v1, v2);
/// let mut weights = graph.edge_map(5);
/// assert_eq!(weights[e1], 5);
/// weights[e1] = 1;
/// assert_eq!(weights[e1], 1);
///
/// // A new edge is immediately available for read and write.
/// let e2 = graph.add_edge(v1, v2);
/// assert_eq!(weights[e2], 5);
/// weights[e2] = 2;
/// assert_eq!(weights[e2], 2);
/// ```
fn edge_map<T: Clone>(&self, default: T) -> EdgeMap<Self::Edge, T>;
/// Returns the degree of `v`, i.e. the number of incident edges, where each loop contributes 2.
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn degree(&self, v: Self::Vertex) -> usize;
/// Returns `true` if there is at least one edge between `v1` and `v2`, and `false` otherwise.
///
/// A vertex is adjacent to itself if and only if it has a loop edge.
///
/// # Panics
///
/// Panics if `v1` or `v2` is not a valid vertex of this graph.
fn are_adjacent(&self, v1: Self::Vertex, v2: Self::Vertex) -> bool;
/// Returns an iterator over all vertices in the graph.
fn vertices(&self) -> impl Iterator<Item = Self::Vertex>;
/// Returns an iterator over all vertices adjacent to `v`.
///
/// Algorithms may prefer this over [`incidences`](Self::incidences) if the edges are not
/// required, since implementors may be able to provide an iterator faster than the trivial
/// mapping. The complexity of this method must not exceed that of
/// [`incidences`](Self::incidences).
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn adjacent_vertices(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Vertex>;
/// Returns the two endpoints of edge `e`, which are identical if and only if `e` is a loop
/// edge.
///
/// # Panics
///
/// Panics if `e` is not a valid edge of this graph.
fn incident_vertices(&self, e: Self::Edge) -> (Self::Vertex, Self::Vertex);
/// Returns an iterator over all edges in the graph.
fn edges(&self) -> impl Iterator<Item = Self::Edge>;
/// Returns an iterator over all edges incident to `v`.
///
/// Algorithms may prefer this over [`incidences`](Self::incidences) if the vertices are not
/// required, since implementors may be able to provide an iterator faster than the trivial
/// mapping. The complexity of this method must not exceed that of
/// [`incidences`](Self::incidences).
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn incident_edges(&self, v: Self::Vertex) -> impl Iterator<Item = Self::Edge>;
/// Returns an iterator over all incidences of `v`, i.e. vertex-edge pairs `(u, e)` such that
/// `u` is adjacent to `v` and `e` is an edge between them.
///
/// Use [`incidence_cursor`](Self::incidence_cursor) instead for a traversal that needs to
/// suspend and resume across other state updates.
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn incidences(&self, v: Self::Vertex) -> impl Iterator<Item = (Self::Vertex, Self::Edge)>;
/// Returns a cursor over all incidences of `v`, analogously to
/// [`incidences`](Self::incidences), initially positioned before the first one.
///
/// See [`IncidenceCursor`](Self::IncidenceCursor) for when to prefer a cursor over
/// [`incidences`](Self::incidences).
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor;
/// Adds a new isolated vertex and returns its handle.
fn add_vertex(&mut self) -> Self::Vertex;
/// Adds a new edge between `v1` and `v2` and returns its handle.
///
/// # Panics
///
/// Panics if `v1` or `v2` is not a valid vertex of this graph.
fn add_edge(&mut self, v1: Self::Vertex, v2: Self::Vertex) -> Self::Edge;
}
/// A trait that adds deletion operations to an undirected graph topology.
///
/// This trait provides methods for deletion of vertices and edges in an undirected graph. These
/// operations will invalidate handles to all deleted vertices and edges. Methods accepting invalid
/// vertices or edges as parameters panic.
pub trait GraphTopologyDeletion: GraphTopology {
/// Deletes the vertex `v` and all its incident edges from the graph. Note that this also
/// invalidates the handles of all edges incident to `v`.
///
/// # Panics
///
/// Panics if `v` is not a valid vertex of this graph.
fn delete_vertex(&mut self, v: Self::Vertex);
/// Deletes the edge `e` from the graph. This operation only invalidates `e` and no other vertex
/// or edge handles.
///
/// # Panics
///
/// Panics if `e` is not a valid edge of this graph.
fn delete_edge(&mut self, e: Self::Edge);
}
/// A cursor for traversing the incidences of a vertex one step at a time.
///
/// Cursors are obtained via [`GraphTopology::incidence_cursor`]. See
/// [`GraphTopology::IncidenceCursor`] for guidance on when to prefer a cursor over
/// [`GraphTopology::incidences`].
pub trait IncidenceCursor<G: GraphTopology + ?Sized> {
/// Advances the cursor and returns the next incidence as `Some((u, e))`, or `None` if the
/// traversal is exhausted.
///
/// # Examples
///
/// ```
/// # use grapherity::prelude::*;
/// # use grapherity::models::Graph;
/// // Constructs a graph with two vertices connected to `v`.
/// let mut graph = Graph::new();
/// let v = graph.add_vertex();
/// for _ in 0..2 {
/// let u = graph.add_vertex();
/// graph.add_edge(u, v);
/// }
///
/// // Iterates over the incidences of `v` with a cursor.
/// let mut c1 = graph.incidence_cursor(v);
/// assert!(c1.next(&graph).is_some());
/// let mut c2 = c1;
/// // Continues iteration with original cursor.
/// assert!(c1.next(&graph).is_some());
/// assert!(c1.next(&graph).is_none());
/// // Iterates over the last incidence again with the copied cursor.
/// assert!(c2.next(&graph).is_some());
/// assert!(c2.next(&graph).is_none());
/// ```
fn next(&mut self, graph: &G) -> Option<(G::Vertex, G::Edge)>;
}