blob: c1035b86193fe306db101e196004ca5db12b55a2 [file] [log] [blame]
#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
use error_stack::{Result, ResultExt};
use crate::{
edge::{EdgeId, EdgeMut},
graph::Graph,
node::{NodeId, NodeMut},
storage::GraphStorage,
Error,
};
impl<S> Graph<S>
where
S: GraphStorage,
{
/// Try to insert a node with the given attributes.
///
/// This is the fallible version of [`Self::insert_node`].
///
/// # Example
///
/// ```
/// use petgraph_dino::DiDinoGraph;
///
/// // we need to explicitly state the type of the edge weights, as we do insert any edges and
/// // therefore cannot infer them.
/// let mut graph = DiDinoGraph::<_, ()>::new();
/// let node = graph.try_insert_node(()).expect("unable to insert node");
/// ```
///
/// # Errors
///
/// If any of the constraints that are required for the graph storage to be valid are violated.
/// This may include things like parallel edges or self loops depending on implementation.
///
/// Refer to the documentation of the underlying storage for more information.
pub fn try_insert_node(&mut self, weight: S::NodeWeight) -> Result<NodeMut<S>, Error> {
let id = self.storage.next_node_id();
self.storage.insert_node(id, weight).change_context(Error)
}
/// Insert a node with the given attributes.
///
/// Depending on the storage type, or if your code is generic over the storage type, you might
/// want to consider using [`Self::try_insert_node`] instead.
///
/// # Example
///
/// ```
/// use petgraph_dino::DiDinoGraph;
///
/// // we need to explicitly state the type of the edge weights, as we do insert any edges and
/// // therefore cannot infer them.
/// let mut graph = DiDinoGraph::<_, ()>::new();
/// let node = graph.insert_node(());
/// ```
///
/// # Rationale
///
/// There are two functions for inserting a node (actually four).
/// [`Self::insert_node`] and [`Self::try_insert_node`].
///
/// Why is that the case?
///
/// The reason is that some storage types might not be able to guarantee that the node can be
/// inserted, but we still want to provide a convenient way to insert a node.
/// Another reason is that this mirrors the API of other libraries and the std, such as the
/// standard library (through [`Vec::push`], or
/// [`std::collections::HashMap::insert`]).
/// These may also panic and do not return a result.
/// But(!) it is important to note that the constraints and reason why they may panic are quite
/// different from the ones of a Graph, as the ones of a Graph can be considered a superset.
/// Not only do they potentially fail on allocation failures, but certain constraints exist such
/// as parallel edges or self loops, which may allow for panics more often than other
/// libraries would.
/// This is why we provide both functions, and you (the user) should decide which one to use.
///
/// # Panics
///
/// If any of the constraints that are required for the graph storage to be valid are violated.
/// This may include things like parallel edges or self loops depending on implementation.
///
/// Refer to the documentation of the underlying storage for more information.
pub fn insert_node(&mut self, weight: S::NodeWeight) -> NodeMut<S> {
self.try_insert_node(weight)
.expect("Constraint violation. Try using `try_insert_node` instead.")
}
}
impl<S> Graph<S>
where
S: GraphStorage,
{
/// Insert a node, where the weight is dependent on the id.
///
/// This is similar to [`Self::try_insert_node`], but instead of providing the weight as a
/// parameter, it can take into account the future id.
///
/// This is the fallible version of [`Self::insert_node_with`].
///
/// # Example
///
/// ```
/// use petgraph_dino::{DiDinoGraph, NodeId};
///
/// struct Node {
/// id: NodeId,
/// }
///
/// // we need to explicitly state the type of the edge weights, as we do insert
/// // any edges and therefore cannot infer them.
/// let mut graph = DiDinoGraph::<_, ()>::new();
/// let node_id = *graph
/// .try_insert_node_with(|id| Node { id: *id })
/// .expect("unable to insert node")
/// .id();
///
/// let node = graph.node(&node_id).expect("node must exist");
///
/// assert_eq!(node.weight().id, node_id);
/// ```
///
/// # Errors
///
/// If any of the constraints that are required for the graph storage to be valid are violated.
/// This may include things like parallel edges or self loops depending on implementation.
///
/// Refer to the documentation of the underlying storage for more information.
pub fn try_insert_node_with(
&mut self,
weight: impl FnOnce(NodeId) -> S::NodeWeight,
) -> Result<NodeMut<S>, Error> {
let id = self.storage.next_node_id();
let weight = weight(id);
self.storage.insert_node(id, weight).change_context(Error)
}
/// Insert a node, where the weight is dependent on the id.
///
/// This is similar to [`Self::insert_node`], but instead of providing the weight as a
/// parameter, it can take into account the future id.
///
/// This is the infallible version of [`Self::try_insert_node_with`], which will panic instead.
///
/// # Example
///
/// ```
/// use petgraph_dino::{DiDinoGraph, NodeId};
///
/// struct Node {
/// id: NodeId,
/// }
///
/// // we need to explicitly state the type of the edge weights, as we do insert any edges and
/// // therefore cannot infer them.
/// let mut graph = DiDinoGraph::<_, ()>::new();
/// let node_id = *graph.insert_node_with(|id| Node { id: *id }).id();
///
/// let node = graph.node(&node_id).expect("node must exist");
///
/// assert_eq!(node.weight().id, node_id);
/// ```
///
/// # Panics
///
/// If any of the constraints that are required for the graph storage to be valid are violated.
/// This may include things like parallel edges or self loops depending on implementation.
///
/// Refer to the documentation of the underlying storage for more information.
pub fn insert_node_with(&mut self, weight: impl FnOnce(NodeId) -> S::NodeWeight) -> NodeMut<S> {
self.try_insert_node_with(weight)
.expect("Constraint violation. Try using `try_insert_node_with` instead.")
}
}
impl<S> Graph<S>
where
S: GraphStorage,
{
/// Try to insert an edge with the given attributes.
///
/// This is the fallible version of [`Self::insert_edge`].
///
/// # Example
///
/// ```
/// use petgraph_dino::DiDinoGraph;
///
/// let mut graph = DiDinoGraph::new();
/// let a = *graph.insert_node(()).id();
/// let b = *graph.insert_node(()).id();
///
/// let edge = graph
/// .try_insert_edge((), &a, &b)
/// .expect("unable to insert edge");
/// ```
///
/// # Errors
///
/// If any of the constraints that are required for the graph storage to be valid are violated.
/// This may include things like parallel edges or self loops depending on implementation.
///
/// Refer to the documentation of the underlying storage for more information.
pub fn try_insert_edge(
&mut self,
weight: S::EdgeWeight,
source: NodeId,
target: NodeId,
) -> Result<EdgeMut<S>, Error> {
let id = self.storage.next_edge_id();
self.storage
.insert_edge(id, weight, source, target)
.change_context(Error)
}
/// Insert an edge with the given attributes.
///
/// Depending on the storage type, or if your code is generic over the storage type, you might
/// want to consider using [`Self::try_insert_edge`] instead.
///
/// # Example
///
/// ```
/// use petgraph_dino::DiDinoGraph;
///
/// let mut graph = DiDinoGraph::new();
/// let a = *graph.insert_node(()).id();
/// let b = *graph.insert_node(()).id();
///
/// let edge = graph.insert_edge((), &a, &b);
/// ```
///
/// # Rationale
///
/// For the rationale behind splitting this into [`Self::insert_edge`] and
/// [`Self::try_insert_edge`], see the documentation of [`Self::insert_node`].
///
/// # Panics
///
/// If any of the constraints that are required for the graph storage to be valid are violated.
/// This may include things like parallel edges or self loops depending on implementation.
pub fn insert_edge(
&mut self,
weight: S::EdgeWeight,
source: NodeId,
target: NodeId,
) -> EdgeMut<S> {
self.try_insert_edge(weight, source, target)
.expect("Constraint violation. Try using `try_insert_edge` instead.")
}
}
impl<S> Graph<S>
where
S: GraphStorage,
{
/// Insert an edge, where the weight is dependent on the id.
///
/// This is similar to [`Self::try_insert_edge`], but instead of providing the weight as a
/// parameter, it will take into account the future id, by calling the given closure with the
/// id.
///
/// This is the fallible version of [`Self::insert_edge_with`].
///
/// # Example
///
/// ```
/// use petgraph_dino::{DiDinoGraph, EdgeId};
///
/// struct Edge {
/// id: EdgeId,
/// }
///
/// let mut graph = DiDinoGraph::new();
/// let a = *graph.insert_node(()).id();
/// let b = *graph.insert_node(()).id();
///
/// let edge_id = *graph
/// .try_insert_edge_with(|id| Edge { id: *id }, &a, &b)
/// .expect("unable to insert edge")
/// .id();
///
/// let edge = graph.edge(&edge_id).expect("edge must exist");
///
/// assert_eq!(edge.weight().id, edge_id);
/// ```
///
/// # Errors
///
/// The same errors as [`Self::try_insert_edge`] may occur.
pub fn try_insert_edge_with(
&mut self,
weight: impl FnOnce(EdgeId) -> S::EdgeWeight,
source: NodeId,
target: NodeId,
) -> Result<EdgeMut<S>, Error> {
let id = self.storage.next_edge_id();
let weight = weight(id);
self.storage
.insert_edge(id, weight, source, target)
.change_context(Error)
}
/// Insert an edge, where the weight is dependent on the id.
///
/// This is similar to [`Self::insert_edge`], but instead of providing the weight as a
/// parameter, it will take into account the future id, by calling the given closure with the
/// id.
///
/// This is the infallible version of [`Self::try_insert_edge_with`], which will panic instead.
///
/// # Example
///
/// ```
/// use petgraph_dino::{DiDinoGraph, EdgeId};
///
/// struct Edge {
/// id: EdgeId,
/// }
///
/// let mut graph = DiDinoGraph::new();
/// let a = *graph.insert_node(()).id();
/// let b = *graph.insert_node(()).id();
///
/// let edge_id = *graph.insert_edge_with(|id| Edge { id: *id }, &a, &b).id();
///
/// let edge = graph.edge(&edge_id).expect("edge must exist");
///
/// assert_eq!(edge.weight().id, edge_id);
/// ```
///
/// # Panics
///
/// The same panics as [`Self::insert_edge`] may occur.
pub fn insert_edge_with(
&mut self,
weight: impl FnOnce(EdgeId) -> S::EdgeWeight,
source: NodeId,
target: NodeId,
) -> EdgeMut<S> {
self.try_insert_edge_with(weight, source, target)
.expect("Constraint violation. Try using `try_insert_edge_with` instead.")
}
}
#[cfg(feature = "alloc")]
impl<S> Graph<S>
where
S: GraphStorage,
S::EdgeWeight: Clone,
{
/// Insert an edge, or update the weight of an existing edge, if it exists.
///
/// If multiple edges exist between the given nodes, all of them will be updated with the given
///
/// Edges are treated as undirected, so the order of the nodes does not matter.
///
/// Unlike [`Self::try_upsert_edge`], this will not return the edge, and instead return a list
/// containing all affected edge identifiers.
///
/// This is the fallible version of [`Self::upsert_edge`].
// TODO: Example
///
/// # Errors
///
/// The same errors as [`Self::try_insert_edge`] may occur.
pub fn try_upsert_edge(
&mut self,
weight: S::EdgeWeight,
source: NodeId,
target: NodeId,
) -> Result<Vec<EdgeId>, Error> {
let mut affected = vec![];
for mut edge in self.storage.edges_between_mut(source, target) {
*edge.weight_mut() = weight.clone();
affected.push(edge.id());
}
if !affected.is_empty() {
return Ok(affected);
}
self.try_insert_edge(weight, source, target)
.map(|edge| vec![edge.id()])
}
/// Insert an edge, or update the weight of an existing edge, if it exists.
///
/// If multiple edges exist between the given nodes, all of them will be updated with the given
/// value.
///
/// This is the infallible version of [`Self::try_upsert_edge`], which will panic instead.
///
/// # Panics
///
/// The same panics as [`Self::try_upsert_edge`] may occur, as well as the ones from
/// [`Self::insert_edge`].
pub fn upsert_edge(
&mut self,
weight: S::EdgeWeight,
source: NodeId,
target: NodeId,
) -> Vec<EdgeId> {
self.try_upsert_edge(weight, source, target)
.expect("Constraint violation. Try using `try_upsert_edge` instead.")
}
/// Insert an edge, or update the weight of an existing edge, if it exists.
///
/// If multiple edges exist between the given nodes, all of them will invoke the given closure
/// with the edge.
///
/// Unlike [`Self::try_upsert_edge_with`], this will not return the edge, and instead return a
/// list containing all affected edge identifiers.
///
/// This is the fallible version of [`Self::upsert_edge_with`].
///
/// # Errors
///
/// The same errors as [`Self::try_insert_edge`] may occur.
pub fn try_upsert_edge_with(
&mut self,
mut on_update: impl FnMut(&mut EdgeMut<S>) -> S::EdgeWeight,
on_insert: impl FnOnce(EdgeId) -> S::EdgeWeight,
source: NodeId,
target: NodeId,
) -> Result<Vec<EdgeId>, Error> {
let mut affected = vec![];
for mut edge in self.storage.edges_between_mut(source, target) {
*edge.weight_mut() = on_update(&mut edge);
affected.push(edge.id());
}
if !affected.is_empty() {
return Ok(affected);
}
self.try_insert_edge_with(on_insert, source, target)
.map(|edge| vec![edge.id()])
}
/// Insert an edge, or update the weight of an existing edge, if it exists.
///
/// If multiple edges exist between the given nodes, all of them will invoke the given closure
/// with the edge.
///
/// This is the infallible version of [`Self::try_upsert_edge_with`], which will panic instead.
///
/// # Panics
///
/// The same panics as [`Self::try_upsert_edge_with`] may occur, as well as the ones from
/// [`Self::insert_edge_with`].
pub fn upsert_edge_with(
&mut self,
on_update: impl FnMut(&mut EdgeMut<S>) -> S::EdgeWeight,
on_insert: impl FnOnce(EdgeId) -> S::EdgeWeight,
source: NodeId,
target: NodeId,
) -> Vec<EdgeId> {
self.try_upsert_edge_with(on_update, on_insert, source, target)
.expect("Constraint violation. Try using `try_upsert_edge_with` instead.")
}
}