1use crate::error::{ProblemError, Result};
7use serde::{de::DeserializeOwned, Serialize};
8use std::fs::File;
9use std::io::{BufReader, BufWriter, Read, Write};
10use std::path::Path;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum FileFormat {
15 Json,
17 JsonCompact,
19}
20
21impl FileFormat {
22 pub fn from_extension(path: &Path) -> Option<Self> {
24 let ext = path.extension()?.to_str()?.to_lowercase();
25 match ext.as_str() {
26 "json" => Some(FileFormat::Json),
27 _ => None,
28 }
29 }
30}
31
32pub fn write_problem<T: Serialize, P: AsRef<Path>>(
50 problem: &T,
51 path: P,
52 format: FileFormat,
53) -> Result<()> {
54 let file = File::create(path.as_ref())
55 .map_err(|e| ProblemError::IoError(format!("Failed to create file: {}", e)))?;
56 let writer = BufWriter::new(file);
57
58 match format {
59 FileFormat::Json => serde_json::to_writer_pretty(writer, problem)
60 .map_err(|e| ProblemError::SerializationError(format!("Failed to write JSON: {}", e))),
61 FileFormat::JsonCompact => serde_json::to_writer(writer, problem)
62 .map_err(|e| ProblemError::SerializationError(format!("Failed to write JSON: {}", e))),
63 }
64}
65
66pub fn read_problem<T: DeserializeOwned, P: AsRef<Path>>(
82 path: P,
83 format: FileFormat,
84) -> Result<T> {
85 let file = File::open(path.as_ref())
86 .map_err(|e| ProblemError::IoError(format!("Failed to open file: {}", e)))?;
87 let reader = BufReader::new(file);
88
89 match format {
90 FileFormat::Json | FileFormat::JsonCompact => serde_json::from_reader(reader)
91 .map_err(|e| ProblemError::SerializationError(format!("Failed to parse JSON: {}", e))),
92 }
93}
94
95pub fn to_json<T: Serialize>(problem: &T) -> Result<String> {
97 serde_json::to_string_pretty(problem)
98 .map_err(|e| ProblemError::SerializationError(format!("Failed to serialize: {}", e)))
99}
100
101pub fn to_json_compact<T: Serialize>(problem: &T) -> Result<String> {
103 serde_json::to_string(problem)
104 .map_err(|e| ProblemError::SerializationError(format!("Failed to serialize: {}", e)))
105}
106
107pub fn from_json<T: DeserializeOwned>(json: &str) -> Result<T> {
109 serde_json::from_str(json)
110 .map_err(|e| ProblemError::SerializationError(format!("Failed to parse JSON: {}", e)))
111}
112
113pub fn read_file<P: AsRef<Path>>(path: P) -> Result<String> {
115 let mut file = File::open(path.as_ref())
116 .map_err(|e| ProblemError::IoError(format!("Failed to open file: {}", e)))?;
117 let mut contents = String::new();
118 file.read_to_string(&mut contents)
119 .map_err(|e| ProblemError::IoError(format!("Failed to read file: {}", e)))?;
120 Ok(contents)
121}
122
123pub fn write_file<P: AsRef<Path>>(path: P, contents: &str) -> Result<()> {
125 let mut file = File::create(path.as_ref())
126 .map_err(|e| ProblemError::IoError(format!("Failed to create file: {}", e)))?;
127 file.write_all(contents.as_bytes())
128 .map_err(|e| ProblemError::IoError(format!("Failed to write file: {}", e)))?;
129 Ok(())
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::models::graph::IndependentSet;
136 use std::fs;
137
138 #[test]
139 fn test_to_json() {
140 let problem = IndependentSet::<i32>::new(3, vec![(0, 1), (1, 2)]);
141 let json = to_json(&problem);
142 assert!(json.is_ok());
143 let json = json.unwrap();
144 assert!(json.contains("graph"));
145 }
146
147 #[test]
148 fn test_from_json() {
149 let problem = IndependentSet::<i32>::new(3, vec![(0, 1), (1, 2)]);
150 let json = to_json(&problem).unwrap();
151 let restored: IndependentSet<i32> = from_json(&json).unwrap();
152 assert_eq!(restored.num_vertices(), 3);
153 assert_eq!(restored.num_edges(), 2);
154 }
155
156 #[test]
157 fn test_json_compact() {
158 let problem = IndependentSet::<i32>::new(3, vec![(0, 1)]);
159 let compact = to_json_compact(&problem).unwrap();
160 let pretty = to_json(&problem).unwrap();
161 assert!(compact.len() < pretty.len());
163 }
164
165 #[test]
166 fn test_file_roundtrip() {
167 let problem = IndependentSet::<i32>::new(4, vec![(0, 1), (1, 2), (2, 3)]);
168 let path = "/tmp/test_problem.json";
169
170 write_problem(&problem, path, FileFormat::Json).unwrap();
172
173 let restored: IndependentSet<i32> = read_problem(path, FileFormat::Json).unwrap();
175 assert_eq!(restored.num_vertices(), 4);
176 assert_eq!(restored.num_edges(), 3);
177
178 fs::remove_file(path).ok();
180 }
181
182 #[test]
183 fn test_file_format_from_extension() {
184 assert_eq!(
185 FileFormat::from_extension(Path::new("test.json")),
186 Some(FileFormat::Json)
187 );
188 assert_eq!(
189 FileFormat::from_extension(Path::new("test.JSON")),
190 Some(FileFormat::Json)
191 );
192 assert_eq!(FileFormat::from_extension(Path::new("test.txt")), None);
193 assert_eq!(FileFormat::from_extension(Path::new("noext")), None);
194 }
195
196 #[test]
197 fn test_read_write_file() {
198 let path = "/tmp/test_io.txt";
199 let contents = "Hello, World!";
200
201 write_file(path, contents).unwrap();
202 let read_back = read_file(path).unwrap();
203
204 assert_eq!(read_back, contents);
205
206 fs::remove_file(path).ok();
207 }
208
209 #[test]
210 fn test_invalid_json() {
211 let result: Result<IndependentSet<i32>> = from_json("not valid json");
212 assert!(result.is_err());
213 }
214}