use crate::backend::traits::{BackendBuilder, BackendConfig, BackendType};
use crate::edge::EdgeIndex;
use crate::graph::Graph;
use crate::node::NodeIndex;
use crate::vgi::error::{VgiError, VgiResult};
use crate::vgi::metadata::{Capability, GraphMetadata, GraphType};
use crate::vgi::traits::{Backend, VirtualGraph};
pub struct SingleMachineBackend<NodeData, EdgeData> {
graph: Graph<NodeData, EdgeData>,
config: BackendConfig,
initialized: bool,
metadata: GraphMetadata,
}
impl<NodeData, EdgeData> SingleMachineBackend<NodeData, EdgeData> {
pub fn inner(&self) -> &Graph<NodeData, EdgeData> {
&self.graph
}
pub fn inner_mut(&mut self) -> &mut Graph<NodeData, EdgeData> {
&mut self.graph
}
pub fn into_inner(self) -> Graph<NodeData, EdgeData> {
self.graph
}
}
impl<NodeData, EdgeData> Default for SingleMachineBackend<NodeData, EdgeData>
where
NodeData: Clone + Send + Sync,
EdgeData: Clone + Send + Sync,
{
fn default() -> Self {
Self {
graph: Graph::directed(),
config: BackendConfig::default(),
initialized: false,
metadata: GraphMetadata::new("single_machine", GraphType::Directed),
}
}
}
impl<NodeData, EdgeData> Backend for SingleMachineBackend<NodeData, EdgeData>
where
NodeData: Clone + Send + Sync,
EdgeData: Clone + Send + Sync,
{
fn name(&self) -> &'static str {
"single_machine"
}
fn version(&self) -> &'static str {
env!("CARGO_PKG_VERSION")
}
fn backend_metadata(&self) -> GraphMetadata {
self.metadata.clone()
}
fn backend_type(&self) -> BackendType {
BackendType::SingleMachine
}
fn is_initialized(&self) -> bool {
self.initialized
}
fn is_healthy(&self) -> bool {
self.initialized
}
fn initialize(&mut self, config: BackendConfig) -> VgiResult<()> {
if self.initialized {
return Err(VgiError::Internal {
message: "Backend already initialized".to_string(),
});
}
self.config = config.clone();
self.graph =
Graph::with_capacity(config.initial_node_capacity, config.initial_edge_capacity);
let mut metadata = GraphMetadata::new("single_machine", config.graph_type)
.with_name("Single Machine Backend")
.with_capability(Capability::IncrementalUpdate)
.with_capability(Capability::DynamicMode)
.with_capability(Capability::StaticMode)
.with_capability(Capability::WeightedEdges)
.with_capability(Capability::SelfLoops)
.with_capability(Capability::NodeAttributes)
.with_capability(Capability::EdgeAttributes);
if config.enable_parallel {
metadata = metadata.with_capability(Capability::Parallel);
}
self.metadata = metadata;
self.initialized = true;
Ok(())
}
fn shutdown(&mut self) -> VgiResult<()> {
self.graph.clear();
self.initialized = false;
Ok(())
}
}
impl<NodeData, EdgeData> VirtualGraph for SingleMachineBackend<NodeData, EdgeData>
where
NodeData: Clone + Send + Sync,
EdgeData: Clone + Send + Sync,
{
type NodeData = NodeData;
type EdgeData = EdgeData;
fn metadata(&self) -> GraphMetadata {
self.metadata.clone()
}
fn has_capability(&self, capability: Capability) -> bool {
matches!(
capability,
Capability::Parallel
| Capability::IncrementalUpdate
| Capability::DynamicMode
| Capability::StaticMode
| Capability::WeightedEdges
| Capability::SelfLoops
| Capability::NodeAttributes
| Capability::EdgeAttributes
)
}
fn node_count(&self) -> usize {
self.graph.node_count()
}
fn edge_count(&self) -> usize {
self.graph.edge_count()
}
fn add_node(&mut self, data: Self::NodeData) -> VgiResult<NodeIndex> {
self.graph.add_node(data).map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn get_node(&self, index: NodeIndex) -> VgiResult<&Self::NodeData> {
self.graph.get_node(index).map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn get_node_mut(&mut self, index: NodeIndex) -> VgiResult<&mut Self::NodeData> {
self.graph
.get_node_mut(index)
.map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn remove_node(&mut self, index: NodeIndex) -> VgiResult<Self::NodeData> {
self.graph
.remove_node(index)
.map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn contains_node(&self, index: NodeIndex) -> bool {
self.graph.contains_node(index)
}
fn nodes(&self) -> impl Iterator<Item = crate::node::NodeRef<'_, Self::NodeData>> {
self.graph.nodes()
}
fn add_edge(
&mut self,
from: NodeIndex,
to: NodeIndex,
data: Self::EdgeData,
) -> VgiResult<EdgeIndex> {
self.graph
.add_edge(from, to, data)
.map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn get_edge(&self, index: EdgeIndex) -> VgiResult<&Self::EdgeData> {
self.graph.get_edge(index).map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn edge_endpoints(&self, index: EdgeIndex) -> VgiResult<(NodeIndex, NodeIndex)> {
self.graph
.edge_endpoints(index)
.map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn remove_edge(&mut self, index: EdgeIndex) -> VgiResult<Self::EdgeData> {
self.graph
.remove_edge(index)
.map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn contains_edge(&self, index: EdgeIndex) -> bool {
self.graph.contains_edge(index)
}
fn has_edge(&self, from: NodeIndex, to: NodeIndex) -> bool {
self.graph.has_edge(from, to)
}
fn edges(&self) -> impl Iterator<Item = crate::edge::EdgeRef<'_, Self::EdgeData>> {
self.graph.edges()
}
fn neighbors(&self, node: NodeIndex) -> impl Iterator<Item = NodeIndex> {
self.graph.neighbors(node)
}
fn incident_edges(&self, node: NodeIndex) -> impl Iterator<Item = EdgeIndex> {
self.graph.incident_edges(node)
}
fn out_degree(&self, node: NodeIndex) -> VgiResult<usize> {
self.graph.out_degree(node).map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn reserve(&mut self, additional_nodes: usize, additional_edges: usize) {
self.graph.reserve(additional_nodes, additional_edges);
}
fn clear(&mut self) {
self.graph.clear();
}
fn update_node(&mut self, index: NodeIndex, data: Self::NodeData) -> VgiResult<Self::NodeData> {
self.graph
.update_node(index, data)
.map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
fn update_edge(&mut self, index: EdgeIndex, data: Self::EdgeData) -> VgiResult<Self::EdgeData> {
self.graph
.update_edge(index, data)
.map_err(|e| VgiError::Internal {
message: e.to_string(),
})
}
}
pub struct SingleMachineBuilder<NodeData, EdgeData> {
config: BackendConfig,
_phantom: std::marker::PhantomData<(NodeData, EdgeData)>,
}
impl<NodeData, EdgeData> Default for SingleMachineBuilder<NodeData, EdgeData> {
fn default() -> Self {
Self {
config: BackendConfig::default(),
_phantom: std::marker::PhantomData,
}
}
}
impl<NodeData, EdgeData> BackendBuilder for SingleMachineBuilder<NodeData, EdgeData>
where
NodeData: Clone + Send + Sync,
EdgeData: Clone + Send + Sync,
{
type Backend = SingleMachineBackend<NodeData, EdgeData>;
fn new() -> Self {
Self::default()
}
fn with_config(mut self, config: BackendConfig) -> Self {
self.config = config;
self
}
fn build(self) -> VgiResult<Self::Backend> {
let mut backend = SingleMachineBackend {
graph: Graph::with_capacity(
self.config.initial_node_capacity,
self.config.initial_edge_capacity,
),
config: self.config.clone(),
initialized: true,
metadata: GraphMetadata::new("single_machine", self.config.graph_type)
.with_name("Single Machine Backend")
.with_capabilities(vec![
Capability::IncrementalUpdate,
Capability::DynamicMode,
Capability::StaticMode,
Capability::WeightedEdges,
Capability::SelfLoops,
Capability::NodeAttributes,
Capability::EdgeAttributes,
]),
};
if self.config.enable_parallel {
backend.metadata = backend.metadata.with_capability(Capability::Parallel);
}
Ok(backend)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::vgi::VirtualGraph;
#[test]
fn test_single_machine_backend() {
let config = BackendConfig::new(GraphType::Directed)
.with_node_capacity(100)
.with_edge_capacity(500);
let mut backend: SingleMachineBackend<String, f64> = SingleMachineBackend::default();
backend.initialize(config).unwrap();
assert!(backend.is_initialized());
assert_eq!(backend.backend_type(), BackendType::SingleMachine);
assert!(backend.has_capability(Capability::IncrementalUpdate));
assert!(!backend.has_capability(Capability::Distributed));
let n1 = backend.add_node("A".to_string()).unwrap();
let n2 = backend.add_node("B".to_string()).unwrap();
let e1 = backend.add_edge(n1, n2, 1.0).unwrap();
assert_eq!(backend.node_count(), 2);
assert_eq!(backend.edge_count(), 1);
assert_eq!(backend.get_node(n1).unwrap(), "A");
assert_eq!(backend.edge_endpoints(e1).unwrap(), (n1, n2));
backend.shutdown().unwrap();
assert!(!backend.is_initialized());
}
#[test]
fn test_single_machine_builder() {
let config = BackendConfig::new(GraphType::Undirected).with_parallel(Some(4));
let backend: SingleMachineBackend<i32, f64> = SingleMachineBuilder::new()
.with_config(config)
.build()
.unwrap();
assert!(backend.is_initialized());
assert!(backend.has_capability(Capability::Parallel));
assert_eq!(backend.graph_type(), GraphType::Undirected);
}
}