use petgraph::graph::{Graph, NodeIndex};
use serde::Deserialize;
use serde_json::Value;
use std::collections::HashMap;
use thiserror::Error;
#[derive(Clone, Debug, Deserialize)]
pub struct NodeData {
pub name: String,
pub text: String,
pub id: String,
}
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
struct RawEntry {
node: NodeData,
rel: Option<Vec<Value>>,
target: Option<NodeData>,
}
#[derive(Debug, Error)]
pub enum Neo4jError {
#[error("invalid JSON: {0}")]
Json(#[from] serde_json::Error),
#[error("expected relationship array of length 3, got {0}")]
BadRelLength(usize),
#[error("unexpected relationship format")]
BadRelFormat,
}
pub fn graph_from_neo4j_json(
json_str: &str,
) -> Result<Graph<NodeData, String>, Neo4jError> {
let entries: Vec<RawEntry> = serde_json::from_str(json_str)?;
let mut graph = Graph::<NodeData, String>::new();
let mut id_to_index: HashMap<String, NodeIndex> = HashMap::new();
for entry in entries {
let src_idx = *id_to_index
.entry(entry.node.id.clone())
.or_insert_with(|| graph.add_node(entry.node.clone()));
if let (Some(rel_arr), Some(target)) = (entry.rel, entry.target) {
if rel_arr.len() != 3 {
return Err(Neo4jError::BadRelLength(rel_arr.len()));
}
let rel_type = rel_arr[1]
.as_str()
.ok_or(Neo4jError::BadRelFormat)?
.to_string();
let tgt_idx = *id_to_index
.entry(target.id.clone())
.or_insert_with(|| graph.add_node(target.clone()));
graph.add_edge(src_idx, tgt_idx, rel_type);
}
}
Ok(graph)
}