xgraph 3.0.0

Graph algorithms for Rust: shortest paths, centrality, connectivity, spanning trees and community detection, over directed, undirected and heterogeneous multigraphs
Documentation
use std::collections::HashMap;

pub use crate::core::view::{EdgeId, NodeId};

/// A struct representing a graph node with data, neighbors, and attributes.
///
/// This struct is generic over the weight type `W` and node data type `N`, allowing flexibility
/// in how nodes are defined and used within a graph.
///
/// # Type Parameters
/// - `W`: The type of the weight associated with edges to neighbors.
/// - `N`: The type of the data stored in the node (must implement `Debug`, `Clone`, `PartialEq`, `Eq`).
///
/// # Fields
/// - `data`: The data associated with the node.
/// - `neighbors`: A list of neighboring nodes, represented as tuples of `(NodeId, EdgeId, W)`.
///   One entry is stored per incident edge, so parallel edges to the same neighbour appear
///   as separate entries distinguished by their edge ID.
/// - `attributes`: A collection of additional attributes for the node, stored as key-value pairs.
///
/// # Examples
///
/// Creating a new node:
/// ```rust
/// use xgraph::core::{EdgeId, NodeId};
/// use xgraph::graph::node::Node;
/// let node = Node::<i32, &str>::new("City");
/// assert_eq!(node.data, "City");
/// assert!(node.neighbors.is_empty());
/// assert!(node.attributes.is_empty());
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Node<W, N> {
    /// The data associated with the node.
    pub data: N,

    /// The list of neighbors, one entry per incident edge, as `(neighbour ID, edge ID, weight)`.
    pub neighbors: Vec<(NodeId, EdgeId, W)>,

    /// A collection of additional attributes for the node, represented as key-value pairs.
    pub attributes: HashMap<String, String>,
}

impl<W, N: Default> Default for Node<W, N> {
    /// Returns a default-initialized `Node`.
    ///
    /// The default node has:
    /// - `data` set to the default value of type `N` (requires `N: Default`).
    /// - `neighbors` as an empty vector.
    /// - `attributes` as an empty `HashMap`.
    ///
    /// # Returns
    /// A new `Node` instance with default values.
    ///
    /// # Examples
    /// ```rust
    /// use xgraph::core::{EdgeId, NodeId};
    /// use xgraph::graph::node::Node;
    /// let node: Node<i32, String> = Node::default();
    /// assert_eq!(node.data, String::new());
    /// assert!(node.neighbors.is_empty());
    /// assert!(node.attributes.is_empty());
    /// ```
    fn default() -> Self {
        Self {
            data: N::default(),
            neighbors: Vec::new(),
            attributes: HashMap::new(),
        }
    }
}

impl<W, N> Node<W, N> {
    /// Creates a new `Node` with the provided data, initializing neighbors and attributes as empty.
    ///
    /// # Arguments
    /// - `data`: The data to associate with the node.
    ///
    /// # Returns
    /// A new `Node` instance with the given data, an empty neighbors list, and an empty attributes map.
    ///
    /// # Examples
    /// ```rust
    /// use xgraph::core::{EdgeId, NodeId};
    /// use xgraph::graph::node::Node;
    /// let node = Node::<f64, i32>::new(42);
    /// assert_eq!(node.data, 42);
    /// assert!(node.neighbors.is_empty());
    /// assert!(node.attributes.is_empty());
    /// ```
    pub fn new(data: N) -> Self {
        Node {
            data,
            neighbors: Vec::new(),
            attributes: HashMap::new(),
        }
    }

    /// Adds a neighbor to the node for one specific incident edge.
    ///
    /// A separate entry is pushed for every edge, so calling this twice with the same
    /// `neighbor_id` but different `edge_id`s records a pair of parallel edges.
    ///
    /// # Arguments
    /// - `neighbor_id`: The ID of the neighboring node.
    /// - `edge_id`: The ID of the edge connecting the two nodes.
    /// - `weight`: The weight of the edge to the neighbor.
    ///
    /// # Examples
    /// ```rust
    /// use xgraph::core::{EdgeId, NodeId};
    /// use xgraph::graph::node::Node;
    /// let mut node = Node::<i32, &str>::new("A");
    /// node.add_neighbor(NodeId::from_index(1), EdgeId::from_index(0), 10);
    /// assert_eq!(node.neighbors[0].0.index(), 1);
    /// ```
    pub fn add_neighbor(&mut self, neighbor_id: NodeId, edge_id: EdgeId, weight: W) {
        self.neighbors.push((neighbor_id, edge_id, weight));
    }

    /// Returns the IDs of the neighbours of this node, without edge IDs or weights.
    ///
    /// A neighbour connected by several parallel edges is yielded once per edge; use a
    /// set if distinct neighbours are needed.
    ///
    /// # Examples
    /// ```rust
    /// use xgraph::core::{EdgeId, NodeId};
    /// use xgraph::graph::node::Node;
    /// let mut node = Node::<i32, &str>::new("A");
    /// node.add_neighbor(NodeId::from_index(1), EdgeId::from_index(0), 10);
    /// node.add_neighbor(NodeId::from_index(1), EdgeId::from_index(1), 20);
    /// let ids: Vec<_> = node.neighbor_ids().map(|id| id.index()).collect();
    /// assert_eq!(ids, vec![1, 1]);
    /// ```
    pub fn neighbor_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
        self.neighbors.iter().map(|&(id, _, _)| id)
    }

    /// Returns the degree of the node, counting parallel edges separately.
    ///
    /// # Examples
    /// ```rust
    /// use xgraph::core::{EdgeId, NodeId};
    /// use xgraph::graph::node::Node;
    /// let mut node = Node::<i32, &str>::new("A");
    /// node.add_neighbor(NodeId::from_index(1), EdgeId::from_index(0), 10);
    /// node.add_neighbor(NodeId::from_index(1), EdgeId::from_index(1), 20);
    /// assert_eq!(node.degree(), 2);
    /// ```
    pub fn degree(&self) -> usize {
        self.neighbors.len()
    }

    /// Gets an attribute value by key.
    ///
    /// # Arguments
    /// - `key`: The key of the attribute to retrieve.
    ///
    /// # Returns
    /// - `Some(&String)` if the attribute exists.
    /// - `None` if the attribute does not exist.
    ///
    /// # Examples
    /// ```rust
    /// use xgraph::core::{EdgeId, NodeId};
    /// use xgraph::graph::node::Node;
    /// let mut node = Node::<i32, &str>::new("A");
    /// node.attributes.insert("color".to_string(), "blue".to_string());
    /// assert_eq!(node.get_attribute("color"), Some(&"blue".to_string()));
    /// ```
    pub fn get_attribute(&self, key: &str) -> Option<&String> {
        self.attributes.get(key)
    }

    /// Sets an attribute with the given key and value.
    ///
    /// # Arguments
    /// - `key`: The key of the attribute to set.
    /// - `value`: The value of the attribute to set.
    ///
    /// # Returns
    /// The value previously stored under `key`, if any.
    ///
    /// # Examples
    /// ```rust
    /// use xgraph::core::{EdgeId, NodeId};
    /// use xgraph::graph::node::Node;
    /// let mut node = Node::<i32, &str>::new("A");
    /// node.set_attribute("color".to_string(), "red".to_string());
    /// assert_eq!(node.get_attribute("color"), Some(&"red".to_string()));
    /// ```
    pub fn set_attribute(&mut self, key: String, value: String) -> Option<String> {
        self.attributes.insert(key, value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::view::{EdgeId, NodeId};

    #[test]
    fn test_node_creation() {
        let node = Node::<i32, &str>::new("Test");
        assert_eq!(node.data, "Test");
        assert!(node.neighbors.is_empty());
        assert!(node.attributes.is_empty());
    }

    #[test]
    fn test_default_node() {
        let node: Node<f64, String> = Node::default();
        assert_eq!(node.data, String::new());
        assert!(node.neighbors.is_empty());
        assert!(node.attributes.is_empty());
    }

    #[test]
    fn test_add_neighbor() {
        let mut node = Node::<u32, i32>::new(1);
        node.add_neighbor(NodeId::from_index(2), EdgeId::from_index(0), 10);
        node.add_neighbor(NodeId::from_index(3), EdgeId::from_index(1), 20);
        assert_eq!(
            node.neighbors,
            vec![
                (NodeId::from_index(2), EdgeId::from_index(0), 10),
                (NodeId::from_index(3), EdgeId::from_index(1), 20)
            ]
        );
        assert_eq!(node.degree(), 2);
        assert_eq!(
            node.neighbor_ids().collect::<Vec<_>>(),
            vec![NodeId::from_index(2), NodeId::from_index(3)]
        );
    }

    #[test]
    fn test_attributes() {
        let mut node = Node::<f32, &str>::new("Node");
        node.set_attribute("size".to_string(), "large".to_string());
        assert_eq!(node.get_attribute("size"), Some(&"large".to_string()));
        assert_eq!(node.get_attribute("color"), None);
    }
}