Skip to main content

graphrust_ffi/
lib.rs

1//! # GraphRust FFI Bindings
2//!
3//! C/C++ bindings for the graphrust library using CXX for modern C++ integration
4//! and cbindgen for C compatibility.
5
6pub mod cxx_bindings;
7
8use graphrust_core::{EdgeWeight, Graph, NodeId};
9
10// Re-export algorithm functions for FFI
11pub use graphrust_algos::{
12    bfs_distances, bfs_predecessors, bfs_traverse, clustering_coefficient, component_count,
13    connected_components, dijkstra, dijkstra_path, dijkstra_with_predecessors, node_component_map,
14    pagerank, pagerank_converge, top_k_pagerank, triangle_count, triangles_per_node,
15};
16
17/// FFI-friendly wrapper for Graph that can be passed across language boundaries
18#[derive(Clone)]
19pub struct FFIGraph {
20    graph: Graph,
21}
22
23impl FFIGraph {
24    /// Creates a new FFI graph with the given number of nodes
25    pub fn new(num_nodes: u32, is_directed: bool) -> Self {
26        FFIGraph {
27            graph: Graph::new(num_nodes, is_directed),
28        }
29    }
30
31    /// Adds an edge to the graph
32    pub fn add_edge(&mut self, from: u32, to: u32, weight: f64) {
33        let from_id = NodeId::new(from);
34        let to_id = NodeId::new(to);
35        let edge_weight = if weight == 1.0 {
36            None
37        } else {
38            Some(EdgeWeight::new(weight))
39        };
40        self.graph.add_edge(from_id, to_id, edge_weight);
41    }
42
43    /// Gets the number of nodes in the graph
44    pub fn num_nodes(&self) -> u32 {
45        self.graph.num_nodes()
46    }
47
48    /// Gets the number of edges in the graph
49    pub fn num_edges(&self) -> u32 {
50        self.graph.num_edges()
51    }
52
53    /// Runs BFS from the given starting node
54    pub fn bfs_traverse_ffi(&self, start: u32) -> Vec<u32> {
55        let traversal = bfs_traverse(&self.graph, NodeId::new(start));
56        traversal.iter().map(|n| n.0).collect()
57    }
58
59    /// Gets distances via BFS from the starting node
60    pub fn bfs_distances_ffi(&self, start: u32) -> Vec<u32> {
61        let distances = bfs_distances(&self.graph, NodeId::new(start));
62        let mut result = vec![u32::MAX; self.graph.num_nodes() as usize];
63        for (node, dist) in distances {
64            result[node.as_usize()] = dist;
65        }
66        result
67    }
68
69    /// Runs Dijkstra's algorithm
70    pub fn dijkstra_ffi(&self, start: u32) -> Vec<f64> {
71        let distances = dijkstra(&self.graph, NodeId::new(start));
72        let mut result = vec![f64::INFINITY; self.graph.num_nodes() as usize];
73        for (node, dist) in distances {
74            result[node.as_usize()] = dist;
75        }
76        result
77    }
78
79    /// Computes PageRank
80    pub fn pagerank_ffi(&self, iterations: u32, damping_factor: f64) -> Vec<f64> {
81        pagerank(&self.graph, iterations, damping_factor)
82    }
83
84    /// Counts triangles in the graph
85    pub fn triangle_count_ffi(&self) -> u64 {
86        triangle_count(&self.graph)
87    }
88
89    /// Gets the number of connected components
90    pub fn component_count_ffi(&self) -> u32 {
91        component_count(&self.graph) as u32
92    }
93
94    /// Gets the clustering coefficient
95    pub fn clustering_coefficient_ffi(&self) -> f64 {
96        clustering_coefficient(&self.graph)
97    }
98}
99
100/// Helper function to create a graph from edge list (for FFI)
101pub fn create_graph_from_edges(
102    num_nodes: u32,
103    is_directed: bool,
104    edges: &[(u32, u32, f64)],
105) -> FFIGraph {
106    let mut graph = FFIGraph::new(num_nodes, is_directed);
107    for &(from, to, weight) in edges {
108        graph.add_edge(from, to, weight);
109    }
110    graph
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_ffi_graph_creation() {
119        let graph = FFIGraph::new(5, true);
120        assert_eq!(graph.num_nodes(), 5);
121        assert_eq!(graph.num_edges(), 0);
122    }
123
124    #[test]
125    fn test_ffi_graph_edges() {
126        let mut graph = FFIGraph::new(5, true);
127        graph.add_edge(0, 1, 1.0);
128        graph.add_edge(1, 2, 2.5);
129        assert_eq!(graph.num_edges(), 2);
130    }
131}