use serde_json::{Map, Value};
use std::fmt;
use std::fs::File;
use std::io::{BufReader, BufWriter};
#[derive(Debug)]
pub enum JsonError {
Io(std::io::Error),
Serialization(serde_json::Error),
}
impl fmt::Display for JsonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JsonError::Io(e) => write!(f, "JSON I/O error: {e}"),
JsonError::Serialization(e) => write!(f, "JSON serialization error: {e}"),
}
}
}
impl std::error::Error for JsonError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
JsonError::Io(e) => Some(e),
JsonError::Serialization(e) => Some(e),
}
}
}
impl From<std::io::Error> for JsonError {
fn from(e: std::io::Error) -> Self {
JsonError::Io(e)
}
}
impl From<serde_json::Error> for JsonError {
fn from(e: serde_json::Error) -> Self {
JsonError::Serialization(e)
}
}
pub fn to_json(map: &Map<String, Value>, path: &str) -> Result<(), JsonError> {
let file = File::create(path)?;
let writer = BufWriter::new(file);
serde_json::to_writer_pretty(writer, map)?;
Ok(())
}
pub fn from_json(path: &str) -> Result<Map<String, Value>, JsonError> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let value: Value = serde_json::from_reader(reader)?;
value.as_object().cloned().ok_or_else(|| {
JsonError::Serialization(serde_json::from_str::<Value>("not_an_object").unwrap_err())
})
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn make_map() -> Map<String, Value> {
let v = json!({
"metadata": { "solver": "RK4", "model": "TestModel" },
"data": {
"time_points": [0.0, 1.0, 2.0],
"profiles": {
"species_0": { "Concentration": [0.0, 0.5, 1.0] }
}
}
});
v.as_object().cloned().unwrap()
}
#[test]
fn test_to_json_creates_file() {
let map = make_map();
let path = "/tmp/chrom_rs_test_json.json";
to_json(&map, path).expect("to_json should succeed");
assert!(std::path::Path::new(path).exists());
}
#[test]
fn test_round_trip() {
let original = make_map();
let path = "/tmp/chrom_rs_test_roundtrip.json";
to_json(&original, path).expect("write");
let loaded = from_json(path).expect("read");
let solver = loaded["metadata"]["solver"].as_str().unwrap();
assert_eq!(solver, "RK4");
let tp = loaded["data"]["time_points"].as_array().unwrap();
assert_eq!(tp.len(), 3);
assert!((tp[1].as_f64().unwrap() - 1.0).abs() < 1e-12);
}
#[test]
fn test_from_json_missing_file() {
let result = from_json("/tmp/does_not_exist_chrom_rs.json");
assert!(matches!(result, Err(JsonError::Io(_))));
}
#[test]
fn test_from_json_not_object() {
let path = "/tmp/chrom_rs_test_array.json";
std::fs::write(path, "[1, 2, 3]").unwrap();
let result = from_json(path);
assert!(matches!(result, Err(JsonError::Serialization(_))));
}
}