1use std::{collections::HashMap, error::Error, fmt};
71
72use petgraph::{
73 graph::NodeIndex,
74 visit::{EdgeRef, IntoEdgeReferences},
75};
76use serde::{Deserialize, Serialize};
77use serde_json::{Map, Value};
78
79use crate::{ForceGraph, ForceGraphHelper};
80
81#[derive(Debug)]
83pub enum JsonParseError {
84 BadFormatting(serde_json::Error),
85 HyperEdges,
86 NodeNotFound(String),
87}
88
89impl fmt::Display for JsonParseError {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match self {
92 Self::BadFormatting(err) => write!(f, "Input not JSON: {err}"),
93 Self::HyperEdges => write!(f, "Graphs with hyperedges are not supported"),
94 Self::NodeNotFound(n) => write!(f, "Node {n} not defined in graph"),
95 }
96 }
97}
98
99impl Error for JsonParseError {}
100
101#[derive(Serialize, Deserialize, Debug)]
102struct JsonGraph {
103 pub graph: InnerJsonGraph,
104}
105
106#[derive(Serialize, Deserialize, Debug)]
107struct InnerJsonGraph {
108 pub nodes: HashMap<String, JsonNode>,
109 pub edges: Option<Vec<JsonEdge>>,
110 #[serde(rename = "hyperedges")]
112 pub _hyperedges: Option<Value>,
113 #[serde(rename = "id")]
114 pub _id: Option<Value>,
115 #[serde(rename = "type")]
116 pub _type: Option<Value>,
117 #[serde(rename = "label")]
118 pub _label: Option<Value>,
119 #[serde(rename = "directed")]
120 pub _directed: Option<Value>,
121 #[serde(rename = "metadata")]
122 pub _metadata: Option<Value>,
123}
124
125#[derive(Serialize, Deserialize, Debug)]
126struct JsonNode {
127 pub label: Option<String>,
128 pub metadata: Option<Value>,
129}
130
131#[derive(Serialize, Deserialize, Debug)]
132struct JsonEdge {
133 pub source: String,
134 pub target: String,
135 #[serde(default)]
136 pub metadata: Value,
137}
138
139pub fn graph_to_json<N: Serialize, E: Serialize>(
141 graph: &ForceGraph<N, E>,
142) -> Result<Value, serde_json::Error> {
143 let mut nodes: HashMap<String, Value> = HashMap::new();
144 let mut edges: Vec<JsonEdge> = Vec::new();
145
146 for node in graph.node_weights() {
147 let metadata: Value = serde_json::to_value(&node.data)?;
148
149 let jnode = JsonNode {
150 label: None,
151 metadata: Some(metadata),
152 };
153
154 let weight = serde_json::to_value(&jnode)?;
155
156 nodes.insert(node.name.to_owned(), weight);
157 }
158
159 for edge in graph.edge_references() {
160 let edge = JsonEdge {
161 source: graph[edge.source()].name.to_owned(),
162 target: graph[edge.target()].name.to_owned(),
163 metadata: serde_json::to_value(edge.weight())?,
164 };
165
166 edges.push(edge);
167 }
168
169 let mut outer_graph: Map<String, Value> = Map::new();
170
171 let mut inner_graph: Map<String, Value> = Map::new();
172
173 inner_graph.insert("nodes".to_string(), serde_json::to_value(&nodes)?);
174 inner_graph.insert("edges".to_string(), serde_json::to_value(&edges)?);
175
176 outer_graph.insert("graph".to_string(), serde_json::to_value(&inner_graph)?);
177
178 Ok(Value::Object(outer_graph))
179}
180
181pub fn graph_from_json(json: impl AsRef<str>) -> Result<ForceGraph<Value, Value>, JsonParseError> {
183 let mut graph: ForceGraph<Value, Value> = ForceGraph::default();
184
185 let json: JsonGraph = match serde_json::from_str(json.as_ref()) {
186 Ok(json) => json,
187 Err(err) => return Err(JsonParseError::BadFormatting(err)),
188 };
189
190 if json.graph._hyperedges.is_some() {
191 return Err(JsonParseError::HyperEdges);
192 }
193
194 for (name, data) in json.graph.nodes {
195 let data = match serde_json::to_value(&data) {
196 Ok(data) => data,
197 Err(err) => return Err(JsonParseError::BadFormatting(err)),
198 };
199
200 graph.add_force_node(name, data);
201 }
202
203 if let Some(edges) = json.graph.edges {
204 for edge in edges {
205 let source = match index_from_name(&edge.source, &graph) {
206 Some(source) => source,
207 None => return Err(JsonParseError::NodeNotFound(edge.source)),
208 };
209
210 let target = match index_from_name(&edge.target, &graph) {
211 Some(source) => source,
212 None => return Err(JsonParseError::NodeNotFound(edge.target)),
213 };
214
215 graph.add_edge(source, target, edge.metadata);
216 }
217 }
218
219 Ok(graph)
220}
221
222fn index_from_name(name: impl AsRef<str>, graph: &ForceGraph<Value, Value>) -> Option<NodeIndex> {
223 let name = name.as_ref().to_string();
224
225 graph.node_indices().find(|&idx| name == graph[idx].name)
226}