1use core::fmt;
89use std::{collections::HashMap, error::Error};
90
91use crate::{ForceGraph, ForceGraphHelper};
92use petgraph::graph::NodeIndex;
93use regex::Regex;
94
95#[derive(Clone, Debug)]
97pub enum GmlParseError {
98 GraphStructure,
99 NoNodes,
100 IdNotNumber,
101 NoId,
102 NoSource,
103 NoTarget,
104 SourceNotNumber,
105 TargetNotNumber,
106 InvalidSource(usize),
107 InvalidTarget(usize),
108 RegexError(String),
109}
110
111impl fmt::Display for GmlParseError {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match self {
114 Self::GraphStructure => {
115 write!(f, "Graph must be structured as \"graph [ [CONTENT] ]\"")
116 }
117 Self::NoNodes => write!(f, "Graph include nodes"),
118 Self::IdNotNumber => write!(f, "Node ids must be a number"),
119 Self::NoId => write!(f, "Nodes must have an id"),
120 Self::NoSource => write!(f, "Edges must have a source"),
121 Self::NoTarget => write!(f, "Edges must have a target"),
122 Self::SourceNotNumber => write!(f, "Edge sources must be numbers"),
123 Self::TargetNotNumber => write!(f, "Edge targets must be numbers"),
124 Self::InvalidSource(s) => write!(f, "Edge source {s} not found in nodes"),
125 Self::InvalidTarget(s) => write!(f, "Edge target {s} not found in nodes"),
126 Self::RegexError(err) => write!(f, "Regex Error: {err}"),
127 }
128 }
129}
130
131impl Error for GmlParseError {}
132
133pub fn graph_from_gml(gml: impl AsRef<str>) -> Result<ForceGraph<(), ()>, GmlParseError> {
135 let gml = gml.as_ref();
136
137 let mut graph = ForceGraph::default();
138 let mut indices: HashMap<usize, NodeIndex<u32>> = HashMap::new();
139
140 let content = match Regex::new(r"graph\s\[([\d\D]+)\]") {
142 Ok(r) => match r.captures(gml) {
143 Some(x) => x[1].to_string(),
144 None => return Err(GmlParseError::GraphStructure),
145 },
146 Err(err) => return Err(GmlParseError::RegexError(err.to_string())),
147 };
148
149 let nodes: Vec<String> = match Regex::new(r"node\s\[([^]]+)\]") {
150 Ok(r) => r
151 .captures_iter(&content)
152 .map(|x| x[1].to_string())
153 .collect(),
154 Err(err) => return Err(GmlParseError::RegexError(err.to_string())),
155 };
156
157 if nodes.is_empty() {
158 return Err(GmlParseError::NoNodes);
159 }
160
161 let id_regex = match Regex::new(r"\sid\s(\d)") {
162 Ok(r) => r,
163 Err(err) => return Err(GmlParseError::RegexError(err.to_string())),
164 };
165
166 let label_regex = match Regex::new(r##"\slabel\s"([^]]+)""##) {
167 Ok(r) => r,
168 Err(err) => return Err(GmlParseError::RegexError(err.to_string())),
169 };
170
171 for node in nodes {
172 let id = match id_regex.captures(&node).map(|x| x[1].to_string()) {
173 Some(id) => id,
174 None => return Err(GmlParseError::NoId),
175 };
176
177 let id: usize = match id.parse() {
178 Ok(id) => id,
179 Err(_) => return Err(GmlParseError::IdNotNumber),
180 };
181
182 let label: String = label_regex
183 .captures(&node)
184 .map(|x| x[1].to_string())
185 .unwrap_or_default();
186
187 indices.insert(id, graph.add_force_node(label, ()));
188 }
189
190 let edges: Vec<String> = match Regex::new(r"edge\s\[([^]]+)\]") {
191 Ok(r) => r
192 .captures_iter(&content)
193 .map(|x| x[1].to_string())
194 .collect(),
195 Err(err) => return Err(GmlParseError::RegexError(err.to_string())),
196 };
197
198 let source_regex = match Regex::new(r"\ssource\s(\d)") {
199 Ok(r) => r,
200 Err(err) => return Err(GmlParseError::RegexError(err.to_string())),
201 };
202
203 let target_regex = match Regex::new(r"\starget\s(\d)") {
204 Ok(r) => r,
205 Err(err) => return Err(GmlParseError::RegexError(err.to_string())),
206 };
207
208 for edge in edges {
209 let source_str = match source_regex.captures(&edge).map(|x| x[1].to_string()) {
210 Some(source) => source,
211 None => return Err(GmlParseError::NoSource),
212 };
213
214 let target_str = match target_regex.captures(&edge).map(|x| x[1].to_string()) {
215 Some(target) => target,
216 None => return Err(GmlParseError::NoTarget),
217 };
218
219 let source: usize = match source_str.parse() {
220 Ok(source) => source,
221 Err(_) => return Err(GmlParseError::SourceNotNumber),
222 };
223
224 let target: usize = match target_str.parse() {
225 Ok(target) => target,
226 Err(_) => return Err(GmlParseError::TargetNotNumber),
227 };
228
229 let source_idx = match indices.get(&source) {
230 Some(idx) => *idx,
231 None => return Err(GmlParseError::InvalidSource(source)),
232 };
233
234 let target_idx = match indices.get(&target) {
235 Some(idx) => *idx,
236 None => return Err(GmlParseError::InvalidSource(target)),
237 };
238
239 graph.add_edge(source_idx, target_idx, ());
240 }
241
242 Ok(graph)
243}
244
245pub fn graph_to_gml<N, E>(graph: &ForceGraph<N, E>) -> String {
247 let mut final_str = String::new();
248
249 final_str.push_str("graph [\n");
250
251 for id in graph.node_indices() {
252 let label = &graph[id].name;
253
254 final_str.push_str(&format!(
255 " node [\n id {}\n label \"{}\"\n ]\n",
256 id.index(),
257 label
258 ));
259 }
260
261 for edge in graph.edge_indices() {
262 let (source, target) = match graph.edge_endpoints(edge) {
263 Some(x) => x,
264 None => continue,
265 };
266
267 final_str.push_str(&format!(
268 " edge [\n source {}\n target {}\n ]\n",
269 source.index(),
270 target.index()
271 ));
272 }
273
274 final_str.push_str("]");
275
276 final_str
277}