use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::Path;
use std::str::FromStr;
use crate::base::{DiGraph, EdgeWeight, Graph, Node};
use crate::error::{GraphError, Result};
#[allow(dead_code)]
pub fn read_edge_list_format<N, E, P>(path: P, weighted: bool) -> Result<Graph<N, E>>
where
N: Node + std::fmt::Debug + FromStr + Clone,
E: EdgeWeight + std::marker::Copy + std::fmt::Debug + std::default::Default + FromStr,
P: AsRef<Path>,
{
let file = File::open(path).map_err(|e| GraphError::Other(format!("Cannot open file: {e}")))?;
let reader = BufReader::new(file);
let mut graph = Graph::new();
for line_result in reader.lines() {
let line =
line_result.map_err(|e| GraphError::Other(format!("Error reading line: {e}")))?;
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 2 {
continue; }
let source_str = parts[0];
let target_str = parts[1];
let source_data = N::from_str(source_str)
.map_err(|_| GraphError::Other(format!("Cannot parse source node: {source_str}")))?;
let target_data = N::from_str(target_str)
.map_err(|_| GraphError::Other(format!("Cannot parse target node: {target_str}")))?;
let edge_weight = if weighted && parts.len() > 2 {
E::from_str(parts[2])
.map_err(|_| GraphError::Other(format!("Cannot parse edge weight: {}", parts[2])))?
} else {
E::default()
};
graph.add_edge(source_data, target_data, edge_weight)?;
}
Ok(graph)
}
#[allow(dead_code)]
pub fn write_edge_list_format<N, E, Ix, P>(
graph: &Graph<N, E, Ix>,
path: P,
weighted: bool,
) -> Result<()>
where
N: Node + std::fmt::Debug + std::fmt::Display + Clone,
E: EdgeWeight
+ std::marker::Copy
+ std::fmt::Debug
+ std::default::Default
+ std::fmt::Display
+ Clone,
Ix: petgraph::graph::IndexType,
P: AsRef<Path>,
{
let mut file =
File::create(path).map_err(|e| GraphError::Other(format!("Cannot create file: {e}")))?;
for edge in graph.edges() {
if weighted {
writeln!(file, "{} {} {}", edge.source, edge.target, edge.weight)
.map_err(|e| GraphError::Other(format!("Error writing line: {e}")))?;
} else {
writeln!(file, "{} {}", edge.source, edge.target)
.map_err(|e| GraphError::Other(format!("Error writing line: {e}")))?;
}
}
Ok(())
}
#[allow(dead_code)]
pub fn read_edge_list_format_digraph<N, E, P>(path: P, weighted: bool) -> Result<DiGraph<N, E>>
where
N: Node + std::fmt::Debug + FromStr + Clone,
E: EdgeWeight + std::marker::Copy + std::fmt::Debug + std::default::Default + FromStr,
P: AsRef<Path>,
{
let file = File::open(path).map_err(|e| GraphError::Other(format!("Cannot open file: {e}")))?;
let reader = BufReader::new(file);
let mut graph = DiGraph::new();
for line_result in reader.lines() {
let line =
line_result.map_err(|e| GraphError::Other(format!("Error reading line: {e}")))?;
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 2 {
continue; }
let source_str = parts[0];
let target_str = parts[1];
let source_data = N::from_str(source_str)
.map_err(|_| GraphError::Other(format!("Cannot parse source node: {source_str}")))?;
let target_data = N::from_str(target_str)
.map_err(|_| GraphError::Other(format!("Cannot parse target node: {target_str}")))?;
let edge_weight = if weighted && parts.len() > 2 {
E::from_str(parts[2])
.map_err(|_| GraphError::Other(format!("Cannot parse edge weight: {}", parts[2])))?
} else {
E::default()
};
graph.add_edge(source_data, target_data, edge_weight)?;
}
Ok(graph)
}
#[allow(dead_code)]
pub fn write_edge_list_format_digraph<N, E, Ix, P>(
graph: &DiGraph<N, E, Ix>,
path: P,
weighted: bool,
) -> Result<()>
where
N: Node + std::fmt::Debug + std::fmt::Display + Clone,
E: EdgeWeight
+ std::marker::Copy
+ std::fmt::Debug
+ std::default::Default
+ std::fmt::Display
+ Clone,
Ix: petgraph::graph::IndexType,
P: AsRef<Path>,
{
let mut file =
File::create(path).map_err(|e| GraphError::Other(format!("Cannot create file: {e}")))?;
for edge in graph.edges() {
if weighted {
writeln!(file, "{} {} {}", edge.source, edge.target, edge.weight)
.map_err(|e| GraphError::Other(format!("Error writing line: {e}")))?;
} else {
writeln!(file, "{} {}", edge.source, edge.target)
.map_err(|e| GraphError::Other(format!("Error writing line: {e}")))?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_edge_list_format_unweighted() {
let mut temp_file = NamedTempFile::new().expect("Operation failed");
writeln!(temp_file, "# This is a comment").expect("Operation failed");
writeln!(temp_file, "1 2").expect("Operation failed");
writeln!(temp_file, "2 3").expect("Operation failed");
writeln!(temp_file, "3 1").expect("Operation failed");
writeln!(temp_file).expect("Operation failed"); temp_file.flush().expect("Operation failed");
let graph: Graph<i32, f64> =
read_edge_list_format(temp_file.path(), false).expect("Operation failed");
assert_eq!(graph.node_count(), 3);
assert_eq!(graph.edge_count(), 3);
}
#[test]
fn test_edge_list_format_weighted() {
let mut temp_file = NamedTempFile::new().expect("Operation failed");
writeln!(temp_file, "1 2 1.5").expect("Operation failed");
writeln!(temp_file, "2 3 2.0").expect("Operation failed");
writeln!(temp_file, "3 1 0.5").expect("Operation failed");
temp_file.flush().expect("Operation failed");
let graph: Graph<i32, f64> =
read_edge_list_format(temp_file.path(), true).expect("Operation failed");
assert_eq!(graph.node_count(), 3);
assert_eq!(graph.edge_count(), 3);
}
#[test]
fn test_digraph_edge_list_format() {
let mut temp_file = NamedTempFile::new().expect("Operation failed");
writeln!(temp_file, "1 2 1.0").expect("Operation failed");
writeln!(temp_file, "2 3 2.0").expect("Operation failed");
temp_file.flush().expect("Operation failed");
let graph: DiGraph<i32, f64> =
read_edge_list_format_digraph(temp_file.path(), true).expect("Operation failed");
assert_eq!(graph.node_count(), 3);
assert_eq!(graph.edge_count(), 2);
}
}