1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use std::io::{Read, Write};
use crate::types::Result;
/// A trait for reading and writing JSON files.
pub trait JsonIO: Sized {
/// Create an instance of the type from a JSON reader.
///
/// # Arguments
///
/// * `reader` - The reader to read from.
///
/// # Returns
///
/// An instance of the type.
///
fn from_json_reader<R: Read>(reader: R) -> Result<Self>;
/// Write the instance to a JSON writer.
///
/// # Arguments
///
/// * `writer` - The writer to write to.
///
fn to_json_writer<W: Write>(&self, writer: W) -> Result<()>;
/// Create an instance of the type from a JSON string.
///
/// # Arguments
///
/// * `json` - The JSON string to parse.
///
/// # Returns
///
/// An instance of the type.
///
fn from_json_string(json: &str) -> Result<Self>;
/// Convert the instance to a JSON string.
///
/// # Returns
///
/// A JSON string representation of the instance.
///
fn to_json_string(&self) -> Result<String>;
/// Create an instance of the type from a JSON file.
///
/// # Arguments
///
/// * `path` - The path to the JSON file.
///
/// # Returns
///
/// An instance of the type.
///
fn from_json_file(path: &str) -> Result<Self>;
/// Write the instance to a JSON file.
///
/// # Arguments
///
/// * `path` - The path to the JSON file.
///
fn to_json_file(&self, path: &str) -> Result<()>;
}
/// A macro to implement the `JsonIO` trait for a given type.
#[macro_export]
macro_rules! impl_json_io {
($type:ty) => {
impl $crate::io::JsonIO for $type {
fn from_json_reader<R: std::io::Read>(reader: R) -> $crate::types::Result<Self> {
// Create a buffered reader.
let reader = std::io::BufReader::new(reader);
// Parse the JSON string.
let json = serde_json::from_reader(reader)?;
// Get the JSON Schema id.
let id = concat!(
paste::paste! { stringify!([<$type:lower>]) },
".schema.json"
);
// Load the JSON Schema validator.
let validator = jsonschema::options()
.with_retriever(&*$crate::assets::JSON_SCHEMA_RETRIEVER)
.build(&serde_json::json!({"$ref": id}))
.map_err(|evidence| $crate::types::Error::Parsing(&format!("Failed to build JSON Schema validator: {}", evidence)))?;
// Validate the JSON against the schema.
let errors: Vec<_> = validator.iter_errors(&json).collect();
if !errors.is_empty() {
let msg = errors
.into_iter()
.map(|evidence| evidence.to_string())
.collect::<Vec<_>>()
.join(", ");
return Err($crate::types::Error::Parsing(&format!(
"JSON Schema validation failed: {}",
msg
)));
}
// Convert the parsed JSON to the type.
serde_json::from_value(json).map_err(Into::into)
}
fn to_json_writer<W: std::io::Write>(&self, writer: W) -> $crate::types::Result<()> {
// Create a buffered writer.
let writer = std::io::BufWriter::new(writer);
// Write the JSON to the writer.
serde_json::to_writer(writer, self)?;
Ok(())
}
fn from_json_string(json: &str) -> $crate::types::Result<Self> {
// Parse the JSON string.
Self::from_json_reader(json.as_bytes())
}
fn to_json_string(&self) -> $crate::types::Result<String> {
// Create a buffer.
let mut buffer = Vec::new();
// Write the JSON to the buffer.
self.to_json_writer(&mut buffer)?;
// Convert the buffer to a string.
String::from_utf8(buffer).map_err(Into::into)
}
fn from_json_file(path: &str) -> $crate::types::Result<Self> {
// Open the file.
let file = std::fs::File::open(path)?;
// Create a buffered reader.
let reader = std::io::BufReader::new(file);
// Parse the JSON string.
Self::from_json_reader(reader)
}
fn to_json_file(&self, path: &str) -> $crate::types::Result<()> {
// Create the file.
let file = std::fs::File::create(path)?;
// Create a buffered writer.
let writer = std::io::BufWriter::new(file);
// Write the JSON to the file.
self.to_json_writer(writer)
}
}
};
}