use crate::edge::{EdgeIndex, EdgeRef};
use crate::node::{NodeIndex, NodeRef};
use crate::vgi::error::{VgiError, VgiResult};
use crate::vgi::metadata::{Capability, GraphMetadata, GraphType};
pub trait GraphRead {
type NodeData;
type EdgeData;
fn metadata(&self) -> GraphMetadata;
fn has_capability(&self, capability: Capability) -> bool {
self.metadata().supports(capability)
}
fn has_capabilities(&self, capabilities: &[Capability]) -> bool {
self.metadata().supports_all(capabilities)
}
fn node_count(&self) -> usize;
fn edge_count(&self) -> usize;
fn is_empty(&self) -> bool {
self.node_count() == 0
}
fn graph_type(&self) -> GraphType {
self.metadata().graph_type
}
fn get_node(&self, index: NodeIndex) -> VgiResult<&Self::NodeData>;
fn contains_node(&self, index: NodeIndex) -> bool;
fn nodes(&self) -> impl Iterator<Item = NodeRef<'_, Self::NodeData>>;
fn get_edge(&self, index: EdgeIndex) -> VgiResult<&Self::EdgeData>;
fn edge_endpoints(&self, index: EdgeIndex) -> VgiResult<(NodeIndex, NodeIndex)>;
fn contains_edge(&self, index: EdgeIndex) -> bool;
fn has_edge(&self, from: NodeIndex, to: NodeIndex) -> bool;
fn edges(&self) -> impl Iterator<Item = EdgeRef<'_, Self::EdgeData>>;
fn neighbors(&self, node: NodeIndex) -> impl Iterator<Item = NodeIndex>;
fn incident_edges(&self, node: NodeIndex) -> impl Iterator<Item = EdgeIndex>;
fn out_degree(&self, node: NodeIndex) -> VgiResult<usize>;
fn in_degree(&self, node: NodeIndex) -> VgiResult<usize> {
self.out_degree(node)
}
fn degree(&self, node: NodeIndex) -> VgiResult<usize> {
self.out_degree(node)
}
}
pub trait GraphUpdate: GraphRead {
fn add_node(&mut self, data: Self::NodeData) -> VgiResult<NodeIndex>;
fn remove_node(&mut self, index: NodeIndex) -> VgiResult<Self::NodeData>;
fn add_edge(
&mut self,
from: NodeIndex,
to: NodeIndex,
data: Self::EdgeData,
) -> VgiResult<EdgeIndex>;
fn remove_edge(&mut self, index: EdgeIndex) -> VgiResult<Self::EdgeData>;
}
pub trait GraphAdvanced: GraphRead {
fn get_node_mut(&mut self, index: NodeIndex) -> VgiResult<&mut Self::NodeData>;
fn reserve(&mut self, additional_nodes: usize, additional_edges: usize);
fn clear(&mut self);
fn update_node(&mut self, index: NodeIndex, data: Self::NodeData) -> VgiResult<Self::NodeData> {
let node = self.get_node_mut(index)?;
Ok(std::mem::replace(node, data))
}
fn update_edge(&mut self, index: EdgeIndex, data: Self::EdgeData) -> VgiResult<Self::EdgeData> {
if !self.contains_edge(index) {
return Err(VgiError::Internal {
message: format!("Edge {:?} not found", index),
});
}
let old_data = self.remove_edge(index)?;
let (from, to) = self.edge_endpoints(index)?;
self.add_edge(from, to, data)?;
Ok(old_data)
}
}
pub trait VirtualGraph: GraphRead + GraphUpdate + GraphAdvanced {}
impl<T> VirtualGraph for T where T: GraphRead + GraphUpdate + GraphAdvanced {}
impl<T> GraphAdvanced for T
where
T: GraphRead + GraphUpdate,
{
fn get_node_mut(&mut self, index: NodeIndex) -> VgiResult<&mut Self::NodeData> {
if !self.contains_node(index) {
return Err(VgiError::Internal {
message: format!("Node {:?} not found", index),
});
}
unimplemented!("get_node_mut requires direct mutable access, which this default implementation cannot provide. Please implement GraphAdvanced explicitly.")
}
fn reserve(&mut self, _additional_nodes: usize, _additional_edges: usize) {
}
fn clear(&mut self) {
let nodes: Vec<_> = self.nodes().map(|n| n.index()).collect();
for node in nodes {
let _ = self.remove_node(node);
}
}
}
pub trait Backend: VirtualGraph + Send + Sync {
fn name(&self) -> &'static str;
fn version(&self) -> &'static str;
fn backend_metadata(&self) -> GraphMetadata;
fn backend_type(&self) -> BackendType;
fn is_initialized(&self) -> bool;
fn is_healthy(&self) -> bool;
fn initialize(&mut self, config: BackendConfig) -> VgiResult<()>;
fn shutdown(&mut self) -> VgiResult<()>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BackendType {
SingleMachine,
Distributed,
ExternalDatabase,
InMemory,
Persistent,
Hybrid,
}
impl std::fmt::Display for BackendType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BackendType::SingleMachine => write!(f, "SingleMachine"),
BackendType::Distributed => write!(f, "Distributed"),
BackendType::ExternalDatabase => write!(f, "ExternalDatabase"),
BackendType::InMemory => write!(f, "InMemory"),
BackendType::Persistent => write!(f, "Persistent"),
BackendType::Hybrid => write!(f, "Hybrid"),
}
}
}
#[derive(Debug, Clone)]
pub struct BackendConfig {
pub graph_type: GraphType,
pub initial_node_capacity: usize,
pub initial_edge_capacity: usize,
pub enable_parallel: bool,
pub parallel_threads: Option<usize>,
pub custom: std::collections::HashMap<String, String>,
}
impl Default for BackendConfig {
fn default() -> Self {
Self {
graph_type: GraphType::Directed,
initial_node_capacity: 1024,
initial_edge_capacity: 4096,
enable_parallel: false,
parallel_threads: None,
custom: std::collections::HashMap::new(),
}
}
}
impl BackendConfig {
pub fn new(graph_type: GraphType) -> Self {
Self {
graph_type,
..Default::default()
}
}
pub fn with_node_capacity(mut self, capacity: usize) -> Self {
self.initial_node_capacity = capacity;
self
}
pub fn with_edge_capacity(mut self, capacity: usize) -> Self {
self.initial_edge_capacity = capacity;
self
}
pub fn with_parallel(mut self, threads: Option<usize>) -> Self {
self.enable_parallel = true;
self.parallel_threads = threads;
self
}
pub fn with_custom(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.custom.insert(key.into(), value.into());
self
}
pub fn get_custom(&self, key: &str) -> Option<&String> {
self.custom.get(key)
}
}
pub trait BackendBuilder: Sized {
type Backend: Backend;
fn new() -> Self;
fn with_config(self, config: BackendConfig) -> Self;
fn build(self) -> VgiResult<Self::Backend>;
}
pub trait GraphBuilder {
type NodeData;
type EdgeData;
type Graph: VirtualGraph<NodeData = Self::NodeData, EdgeData = Self::EdgeData>;
fn directed() -> Self::Graph;
fn undirected() -> Self::Graph;
fn with_type(graph_type: GraphType) -> Self::Graph;
fn with_capacity(nodes: usize, edges: usize) -> Self::Graph;
fn with_metadata(metadata: GraphMetadata) -> Self::Graph;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::Graph;
use crate::graph::traits::GraphOps;
#[test]
fn test_graph_type_display() {
assert_eq!(GraphType::Directed.to_string(), "Directed");
assert_eq!(GraphType::Undirected.to_string(), "Undirected");
assert_eq!(GraphType::Mixed.to_string(), "Mixed");
}
#[test]
fn test_graph_read() {
let mut graph = Graph::<String, f64>::directed();
let n1 = graph.add_node("A".to_string()).unwrap();
let n2 = graph.add_node("B".to_string()).unwrap();
graph.add_edge(n1, n2, 1.0).unwrap();
assert_eq!(graph.node_count(), 2);
assert_eq!(graph.edge_count(), 1);
assert_eq!(graph.get_node(n1).unwrap(), "A");
assert!(graph.contains_node(n1));
let neighbors: Vec<_> = graph.neighbors(n1).collect();
assert_eq!(neighbors, vec![n2]);
}
#[test]
fn test_graph_update() {
let mut graph = Graph::<String, f64>::directed();
let n1 = graph.add_node("A".to_string()).unwrap();
let n2 = graph.add_node("B".to_string()).unwrap();
let e1 = graph.add_edge(n1, n2, 1.0).unwrap();
assert_eq!(graph.node_count(), 2);
assert_eq!(graph.edge_count(), 1);
let removed = graph.remove_edge(e1).unwrap();
assert_eq!(removed, 1.0);
}
#[test]
fn test_virtual_graph_basic() {
let mut graph = Graph::<String, f64>::directed();
let metadata = graph.metadata();
assert_eq!(metadata.graph_type, GraphType::Directed);
let n1 = graph.add_node("A".to_string()).unwrap();
let n2 = graph.add_node("B".to_string()).unwrap();
assert_eq!(graph.node_count(), 2);
assert!(graph.contains_node(n1));
assert!(graph.contains_node(n2));
assert_eq!(graph.get_node(n1).unwrap(), "A");
assert_eq!(graph.get_node(n2).unwrap(), "B");
let e1 = graph.add_edge(n1, n2, 1.0).unwrap();
assert_eq!(graph.edge_count(), 1);
assert!(graph.contains_edge(e1));
let neighbors: Vec<_> = graph.neighbors(n1).collect();
assert_eq!(neighbors, vec![n2]);
assert_eq!(graph.out_degree(n1).unwrap(), 1);
}
#[test]
fn test_virtual_graph_remove() {
let mut graph = Graph::<i32, f64>::undirected();
let n1 = graph.add_node(1).unwrap();
let n2 = graph.add_node(2).unwrap();
let e1 = graph.add_edge(n1, n2, 1.0).unwrap();
let edge_data = graph.remove_edge(e1).unwrap();
assert_eq!(edge_data, 1.0);
assert_eq!(graph.edge_count(), 0);
let node_data = graph.remove_node(n1).unwrap();
assert_eq!(node_data, 1);
assert_eq!(graph.node_count(), 1);
}
}