Skip to main content

problemreductions/
io.rs

1//! File I/O utilities for problem serialization.
2//!
3//! This module provides functions for reading and writing problems
4//! to various file formats using serde.
5
6use 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/// Supported file formats.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum FileFormat {
15    /// JSON format (human-readable).
16    Json,
17    /// Compact JSON format (no pretty-printing).
18    JsonCompact,
19}
20
21impl FileFormat {
22    /// Detect file format from file extension.
23    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
32/// Write a problem to a file.
33///
34/// # Arguments
35///
36/// * `problem` - The problem to write
37/// * `path` - The file path to write to
38/// * `format` - The file format to use
39///
40/// # Example
41///
42/// ```no_run
43/// use problemreductions::io::{write_problem, FileFormat};
44/// use problemreductions::models::graph::MaximumIndependentSet;
45/// use problemreductions::topology::SimpleGraph;
46///
47/// let problem = MaximumIndependentSet::new(SimpleGraph::new(3, vec![(0, 1), (1, 2)]), vec![1i32; 3]);
48/// write_problem(&problem, "problem.json", FileFormat::Json).unwrap();
49/// ```
50pub fn write_problem<T: Serialize, P: AsRef<Path>>(
51    problem: &T,
52    path: P,
53    format: FileFormat,
54) -> Result<()> {
55    let file = File::create(path.as_ref())
56        .map_err(|e| ProblemError::IoError(format!("Failed to create file: {}", e)))?;
57    let writer = BufWriter::new(file);
58
59    match format {
60        FileFormat::Json => serde_json::to_writer_pretty(writer, problem)
61            .map_err(|e| ProblemError::SerializationError(format!("Failed to write JSON: {}", e))),
62        FileFormat::JsonCompact => serde_json::to_writer(writer, problem)
63            .map_err(|e| ProblemError::SerializationError(format!("Failed to write JSON: {}", e))),
64    }
65}
66
67/// Read a problem from a file.
68///
69/// # Arguments
70///
71/// * `path` - The file path to read from
72/// * `format` - The file format to use
73///
74/// # Example
75///
76/// ```no_run
77/// use problemreductions::io::{read_problem, FileFormat};
78/// use problemreductions::models::graph::MaximumIndependentSet;
79/// use problemreductions::topology::SimpleGraph;
80///
81/// let problem: MaximumIndependentSet<SimpleGraph, i32> = read_problem("problem.json", FileFormat::Json).unwrap();
82/// ```
83pub fn read_problem<T: DeserializeOwned, P: AsRef<Path>>(path: P, format: FileFormat) -> Result<T> {
84    let file = File::open(path.as_ref())
85        .map_err(|e| ProblemError::IoError(format!("Failed to open file: {}", e)))?;
86    let reader = BufReader::new(file);
87
88    match format {
89        FileFormat::Json | FileFormat::JsonCompact => serde_json::from_reader(reader)
90            .map_err(|e| ProblemError::SerializationError(format!("Failed to parse JSON: {}", e))),
91    }
92}
93
94/// Serialize a problem to a JSON string.
95pub fn to_json<T: Serialize>(problem: &T) -> Result<String> {
96    serde_json::to_string_pretty(problem)
97        .map_err(|e| ProblemError::SerializationError(format!("Failed to serialize: {}", e)))
98}
99
100/// Serialize a problem to a compact JSON string.
101pub fn to_json_compact<T: Serialize>(problem: &T) -> Result<String> {
102    serde_json::to_string(problem)
103        .map_err(|e| ProblemError::SerializationError(format!("Failed to serialize: {}", e)))
104}
105
106/// Deserialize a problem from a JSON string.
107pub fn from_json<T: DeserializeOwned>(json: &str) -> Result<T> {
108    serde_json::from_str(json)
109        .map_err(|e| ProblemError::SerializationError(format!("Failed to parse JSON: {}", e)))
110}
111
112/// Read a file to a string.
113pub fn read_file<P: AsRef<Path>>(path: P) -> Result<String> {
114    let mut file = File::open(path.as_ref())
115        .map_err(|e| ProblemError::IoError(format!("Failed to open file: {}", e)))?;
116    let mut contents = String::new();
117    file.read_to_string(&mut contents)
118        .map_err(|e| ProblemError::IoError(format!("Failed to read file: {}", e)))?;
119    Ok(contents)
120}
121
122/// Write a string to a file.
123pub fn write_file<P: AsRef<Path>>(path: P, contents: &str) -> Result<()> {
124    let mut file = File::create(path.as_ref())
125        .map_err(|e| ProblemError::IoError(format!("Failed to create file: {}", e)))?;
126    file.write_all(contents.as_bytes())
127        .map_err(|e| ProblemError::IoError(format!("Failed to write file: {}", e)))?;
128    Ok(())
129}
130
131#[cfg(test)]
132#[path = "unit_tests/io.rs"]
133mod tests;