wolf-graph 0.1.0

Data structures and algorithms for working with graphs with reference or value semantics.
Documentation
use std::{borrow::Cow, sync::{Arc, RwLock}};

#[cfg(feature = "serde")]
use serde::{ser::{Serialize, Serializer}, de::{Deserialize, Deserializer}};

use crate::{EdgeID, Edges, NodeID};

#[derive(Debug, Clone)]
pub struct Node<NData>
where
    NData: Clone + 'static,
{
    pub id: NodeID,
    pub in_edges: Arc<RwLock<Edges>>,
    pub out_edges: Arc<RwLock<Edges>>,
    pub data: Cow<'static, NData>,
}

impl<NData> Node<NData>
where
    NData: Clone + 'static,
{
    pub fn new(id: NodeID, data: NData) -> Self {
        Node {
            id,
            in_edges: Arc::new(RwLock::new(Edges::new())),
            out_edges: Arc::new(RwLock::new(Edges::new())),
            data: Cow::Owned(data),
        }
    }

    pub fn setting_data(&self, data: NData) -> Self {
        Node {
            id: self.id.clone(),
            in_edges: self.in_edges.clone(),
            out_edges: self.out_edges.clone(),
            data: Cow::Owned(data),
        }
    }

    fn _update_in_edges(&mut self, f: impl FnOnce(&mut Edges)) {
        let mut in_edges = self.in_edges.write().unwrap().clone();
        f(&mut in_edges);
        self.in_edges = Arc::new(RwLock::new(in_edges));
    }

    fn _update_out_edges(&mut self, f: impl FnOnce(&mut Edges)) {
        let mut out_edges = self.out_edges.write().unwrap().clone();
        f(&mut out_edges);
        self.out_edges = Arc::new(RwLock::new(out_edges));
    }

    pub fn inserting_in_edge(&self, edge_id: EdgeID) -> Self {
        let mut new_self = self.clone();
        new_self._update_in_edges(|in_edges| {
            in_edges.insert(edge_id);
        });
        new_self
    }

    pub fn removing_in_edge(&self, edge_id: &EdgeID) -> Self {
        let mut new_self = self.clone();
        new_self._update_in_edges(|in_edges| {
            in_edges.remove(edge_id);
        });
        new_self
    }

    pub fn inserting_out_edge(&self, edge_id: EdgeID) -> Self {
        let mut new_self = self.clone();
        new_self._update_out_edges(|out_edges| {
            out_edges.insert(edge_id);
        });
        new_self
    }

    pub fn removing_out_edge(&self, edge_id: &EdgeID) -> Self {
        let mut new_self = self.clone();
        new_self._update_out_edges(|out_edges| {
            out_edges.remove(edge_id);
        });
        new_self
    }
}

impl<NData> PartialEq for Node<NData>
where
    NData: Clone + PartialEq + 'static,
{
    fn eq(&self, other: &Self) -> bool {
        // We do not compare the in edges or out edges
        // because they are denormalized and derived from
        // the edges of the graph.
        self.id == other.id && self.data == other.data
    }
}

#[cfg(feature = "serde")]
impl<NData> Serialize for Node<NData>
where
    NData: Clone + Serialize + 'static,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // If there is no data, we serialize just the node ID
        if std::mem::size_of::<NData>() == 0 {
            self.id.serialize(serializer)
        } else {
            // Otherwise, we serialize the node ID and the data as a tuple
            (&self.id, &self.data).serialize(serializer)
        }
    }
}

#[cfg(feature = "serde")]
impl<'de, NData> Deserialize<'de> for Node<NData>
where
    NData: Clone + Deserialize<'de> + 'static + Default,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        if std::mem::size_of::<NData>() == 0 {
            let node_id = NodeID::deserialize(deserializer)?;
            Ok(Node::new(node_id, NData::default()))
        } else {
            let (node_id, data) = <(NodeID, NData)>::deserialize(deserializer)?;
            Ok(Node::new(node_id, data))
        }
    }
}

#[cfg(all(test, feature = "serde", feature = "serde_json"))]
mod tests {
    use crate::nid;

    use super::*;
    use serde_test::{Token, assert_tokens, Configure};

    #[test]
    fn node_with_data_round_trip() {
        let node: Node<i32> = Node::new(nid!("NodeA"), 99);

        assert_tokens(
            &node.compact(),
            &[
                Token::Tuple { len: 2 },
                Token::Str("NodeA"),
                Token::I32(99),
                Token::TupleEnd,
            ],
        );
    }

    #[test]
    fn node_with_unit_data_round_trip() {
        let node: Node<()> = Node::new(nid!("NodeB"), ());

        assert_tokens(
            &node.compact(),
            &[
                Token::Str("NodeB"),
            ],
        );
    }

    #[test]
    fn node_with_data_json_round_trip() {
        let node: Node<i32> = Node::new(nid!("NodeA"), 99);
        let json = serde_json::to_string(&node).unwrap();
        assert_eq!(json, r#"["NodeA",99]"#);

        let deserialized_node: Node<i32> = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized_node, node);
    }

    #[test]
    fn node_with_unit_data_json_round_trip() {
        let node: Node<()> = Node::new(nid!("NodeB"), ());

        let json = serde_json::to_string(&node).unwrap();
        assert_eq!(json, r#""NodeB""#);

        let deserialized_node: Node<()> = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized_node, node);
    }
}