Skip to main content

fso_graph_builder/input/
gdl.rs

1use crate::graph::csr::{CsrLayout, DirectedCsrGraph, NodeValues, UndirectedCsrGraph};
2use crate::index::Idx;
3use crate::input::EdgeList;
4
5use gdl::CypherValue;
6
7#[cfg(feature = "dotgraph")]
8use crate::input::DotGraph;
9#[cfg(feature = "dotgraph")]
10use linereader::LineReader;
11#[cfg(feature = "dotgraph")]
12use std::fmt::Write;
13#[cfg(feature = "dotgraph")]
14use std::hash::Hash;
15
16/// A wrapper around [`gdl::CypherValue`] to allow custom From implementations.
17pub struct MyCypherValue<'a>(&'a CypherValue);
18
19impl From<MyCypherValue<'_>> for () {
20    fn from(_: MyCypherValue) -> Self {}
21}
22
23macro_rules! impl_from_cypher_value {
24    ($enum:path, $ty:ty) => {
25        impl<'a> ::std::convert::From<$crate::input::gdl::MyCypherValue<'a>> for $ty {
26            fn from(cv: $crate::input::gdl::MyCypherValue) -> Self {
27                if let $enum(f) = cv.0 {
28                    *f as $ty
29                } else {
30                    panic!("expected {} value", stringify!($ty))
31                }
32            }
33        }
34    };
35}
36
37impl_from_cypher_value!(CypherValue::Float, f32);
38impl_from_cypher_value!(CypherValue::Float, f64);
39impl_from_cypher_value!(CypherValue::Integer, i32);
40impl_from_cypher_value!(CypherValue::Integer, i64);
41impl_from_cypher_value!(CypherValue::Integer, isize);
42impl_from_cypher_value!(CypherValue::Integer, u32);
43impl_from_cypher_value!(CypherValue::Integer, u64);
44impl_from_cypher_value!(CypherValue::Integer, usize);
45
46impl<'gdl, NI, EV> From<&'gdl gdl::Graph> for EdgeList<NI, EV>
47where
48    NI: Idx,
49    EV: From<MyCypherValue<'gdl>> + Default + Send + Sync,
50{
51    fn from(gdl_graph: &'gdl gdl::Graph) -> Self {
52        let edges = gdl_graph
53            .relationships()
54            .map(|r| {
55                let source = gdl_graph.get_node(r.source()).unwrap().id();
56                let target = gdl_graph.get_node(r.target()).unwrap().id();
57
58                let value = if let Some(k) = r.property_keys().next() {
59                    EV::from(MyCypherValue(r.property_value(k).unwrap()))
60                } else {
61                    EV::default()
62                };
63
64                (NI::new(source), NI::new(target), value)
65            })
66            .collect::<Vec<_>>();
67
68        EdgeList::new(edges)
69    }
70}
71
72impl<'gdl, NV> From<&'gdl gdl::Graph> for NodeValues<NV>
73where
74    NV: From<MyCypherValue<'gdl>> + Default + Send + Sync,
75{
76    fn from(gdl_graph: &'gdl gdl::Graph) -> Self {
77        let mut node_values = Vec::with_capacity(gdl_graph.node_count());
78        node_values.resize_with(gdl_graph.node_count(), || NV::default());
79
80        gdl_graph.nodes().for_each(|n| {
81            if let Some(k) = n.property_keys().next() {
82                node_values[n.id()] = NV::from(MyCypherValue(n.property_value(k).unwrap()));
83            }
84        });
85
86        NodeValues::new(node_values)
87    }
88}
89
90#[cfg(feature = "dotgraph")]
91impl<NI, Label> From<&gdl::Graph> for DotGraph<NI, Label>
92where
93    NI: Idx,
94    Label: Idx + Hash,
95{
96    /// Converts the given GDL graph into a .graph input string.
97    ///
98    /// Node labels need to be numeric, however GDL does not support numeric
99    /// labels. In order to circumvent this, node labels need to be prefixed
100    /// with a single character, e.g. `(n:L0)` to declare label `0`.
101    fn from(gdl_graph: &gdl::Graph) -> Self {
102        fn degree(gdl_graph: &gdl::Graph, node: &gdl::graph::Node) -> usize {
103            let mut degree = 0;
104
105            for rel in gdl_graph.relationships() {
106                if rel.source() == node.variable() {
107                    degree += 1;
108                }
109                if rel.target() == node.variable() {
110                    degree += 1;
111                }
112            }
113            degree
114        }
115
116        let header = format!(
117            "t {} {}",
118            gdl_graph.node_count(),
119            gdl_graph.relationship_count()
120        );
121
122        let mut nodes_string = String::from("");
123
124        let mut sorted_nodes = gdl_graph.nodes().collect::<Vec<_>>();
125        sorted_nodes.sort_by_key(|node| node.id());
126
127        for node in sorted_nodes {
128            let id = node.id();
129            let label = node.labels().next().expect("Single label expected");
130            let degree = degree(gdl_graph, node);
131            let _ = writeln!(nodes_string, "v {id} {} {degree}", &label[1..]);
132        }
133
134        let mut rels_string = String::from("");
135
136        let mut sorted_rels = gdl_graph.relationships().collect::<Vec<_>>();
137        sorted_rels.sort_by_key(|rel| (rel.source(), rel.target()));
138
139        for rel in sorted_rels {
140            let source_id = gdl_graph
141                .get_node(rel.source())
142                .expect("Source expected")
143                .id();
144            let target_id = gdl_graph
145                .get_node(rel.target())
146                .expect("Target expected")
147                .id();
148            let _ = writeln!(rels_string, "e {source_id} {target_id}");
149        }
150
151        let input = format!("{header}\n{nodes_string}{rels_string}");
152        let reader = LineReader::new(input.as_bytes());
153
154        DotGraph::<NI, Label>::try_from(reader).expect("GDL to .graph conversion failed")
155    }
156}
157
158impl<'a, NI, NV, EV> From<(&'a gdl::Graph, CsrLayout)> for DirectedCsrGraph<NI, NV, EV>
159where
160    NI: Idx,
161    NV: From<MyCypherValue<'a>> + Default + Copy + Send + Sync,
162    EV: From<MyCypherValue<'a>> + Default + Copy + Send + Sync,
163{
164    fn from((gdl_graph, csr_layout): (&'a gdl::Graph, CsrLayout)) -> Self {
165        let node_values = NodeValues::from(gdl_graph);
166        let edge_list = EdgeList::from(gdl_graph);
167        DirectedCsrGraph::from((node_values, edge_list, csr_layout))
168    }
169}
170
171impl<NI, NV, EV> From<(gdl::Graph, CsrLayout)> for DirectedCsrGraph<NI, NV, EV>
172where
173    NI: Idx,
174    for<'a> NV: From<MyCypherValue<'a>> + Default + Copy + Send + Sync,
175    for<'a> EV: From<MyCypherValue<'a>> + Default + Copy + Send + Sync,
176{
177    fn from((gdl_graph, csr_layout): (gdl::Graph, CsrLayout)) -> Self {
178        let node_values = NodeValues::from(&gdl_graph);
179        let edge_list = EdgeList::from(&gdl_graph);
180        DirectedCsrGraph::from((node_values, edge_list, csr_layout))
181    }
182}
183
184impl<'a, NI, NV, EV> From<(&'a gdl::Graph, CsrLayout)> for UndirectedCsrGraph<NI, NV, EV>
185where
186    NI: Idx,
187    NV: From<MyCypherValue<'a>> + Default + Copy + Send + Sync,
188    EV: From<MyCypherValue<'a>> + Default + Copy + Send + Sync,
189{
190    fn from((gdl_graph, csr_layout): (&'a gdl::Graph, CsrLayout)) -> Self {
191        let node_values = NodeValues::from(gdl_graph);
192        let edge_list = EdgeList::from(gdl_graph);
193        UndirectedCsrGraph::from((node_values, edge_list, csr_layout))
194    }
195}
196
197impl<NI, NV, EV> From<(gdl::Graph, CsrLayout)> for UndirectedCsrGraph<NI, NV, EV>
198where
199    NI: Idx,
200    for<'a> NV: From<MyCypherValue<'a>> + Default + Copy + Send + Sync,
201    for<'a> EV: From<MyCypherValue<'a>> + Default + Copy + Send + Sync,
202{
203    fn from((gdl_graph, csr_layout): (gdl::Graph, CsrLayout)) -> Self {
204        let node_values = NodeValues::from(&gdl_graph);
205        let edge_list = EdgeList::from(&gdl_graph);
206        UndirectedCsrGraph::from((node_values, edge_list, csr_layout))
207    }
208}