1use serde::{Deserialize, Serialize};
2
3use std::fs::File;
4use std::io::Read;
5use std::path::Path;
6
7pub mod v1_2;
8pub mod v1_3;
9
10#[derive(thiserror::Error, Debug)]
12pub enum Error {
13 #[error("error reading file")]
14 Io(#[from] ::std::io::Error),
15 #[error("error serializing YAML")]
16 Yaml(#[from] ::serde_yaml::Error),
17 #[error("error serializing JSON")]
18 Json(#[from] ::serde_json::Error),
19}
20
21#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
26#[serde(tag = "version")]
27pub enum Spec {
28 #[allow(non_camel_case_types)]
34 #[serde(rename = "1.2")]
35 V1_2(v1_2::Log),
36
37 #[allow(non_camel_case_types)]
43 #[serde(rename = "1.3")]
44 V1_3(v1_3::Log),
45}
46
47#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
48pub struct Har {
49 pub log: Spec,
50}
51
52pub fn from_path<P>(path: P) -> Result<Har, Error>
54where
55 P: AsRef<Path>,
56{
57 from_reader(File::open(path)?)
58}
59
60pub fn from_reader<R>(read: R) -> Result<Har, Error>
62where
63 R: Read,
64{
65 Ok(serde_json::from_reader::<R, Har>(read)?)
66}
67
68pub fn to_yaml(spec: &Har) -> Result<String, Error> {
70 Ok(serde_yaml::to_string(spec)?)
71}
72
73pub fn to_json(spec: &Har) -> Result<String, Error> {
75 Ok(serde_json::to_string_pretty(spec)?)
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use glob::glob;
82 use std::fs::File;
83 use std::io::Write;
84
85 const FIXTURES_GLOB: &str = "tests/fixtures/*.har";
86
87 fn read_file<P>(path: P) -> String
89 where
90 P: AsRef<Path>,
91 {
92 let mut f = File::open(path).unwrap();
93 let mut content = String::new();
94 f.read_to_string(&mut content).unwrap();
95 content
96 }
97
98 fn write_to_file<P>(path: P, filename: &str, data: &str)
100 where
101 P: AsRef<Path> + std::fmt::Debug,
102 {
103 println!(" Saving string to {:?}...", path);
104 std::fs::create_dir_all(&path).unwrap();
105 let full_filename = path.as_ref().to_path_buf().join(filename);
106 let mut f = File::create(full_filename).unwrap();
107 f.write_all(data.as_bytes()).unwrap();
108 }
109
110 fn convert_yaml_str_to_json(yaml_str: &str) -> String {
112 let yaml: serde_yaml::Value = serde_yaml::from_str(yaml_str).unwrap();
113 let json: serde_json::Value = serde_yaml::from_value(yaml).unwrap();
114 serde_json::to_string_pretty(&json).unwrap()
115 }
116
117 fn compare_spec_through_json(
129 input_file: &Path,
130 save_path_base: &Path,
131 ) -> (String, String, String) {
132 let spec_yaml_str = read_file(input_file);
137 let spec_json_str = convert_yaml_str_to_json(&spec_yaml_str);
139
140 let parsed_spec = from_path(input_file).unwrap();
145 let parsed_spec_json: serde_json::Value = serde_json::to_value(parsed_spec).unwrap();
147 let parsed_spec_json_str: String = serde_json::to_string_pretty(&parsed_spec_json).unwrap();
149
150 let api_filename = input_file
152 .file_name()
153 .unwrap()
154 .to_str()
155 .unwrap()
156 .replace(".yaml", ".json");
157
158 let mut save_path = save_path_base.to_path_buf();
159 save_path.push("yaml_to_json");
160 write_to_file(&save_path, &api_filename, &spec_json_str);
161
162 let mut save_path = save_path_base.to_path_buf();
163 save_path.push("yaml_to_spec_to_json");
164 write_to_file(&save_path, &api_filename, &parsed_spec_json_str);
165
166 (api_filename, parsed_spec_json_str, spec_json_str)
168 }
169
170 #[test]
172 fn can_find_test_fixtures() {
173 let fixture_count = glob(FIXTURES_GLOB)
174 .expect("Failed to read glob pattern")
175 .filter(|e| e.is_ok())
176 .count();
177 assert_ne!(0, fixture_count);
178 }
179
180 #[test]
182 fn can_deserialize() {
183 for entry in glob(FIXTURES_GLOB).expect("Failed to read glob pattern") {
184 let entry = entry.unwrap();
185 let path = entry.as_path();
186 println!("Testing if {:?} is deserializable", path);
188 from_path(path).unwrap();
189 }
190 }
191
192 #[test]
193 fn can_deserialize_and_reserialize() {
194 let save_path_base: std::path::PathBuf =
195 ["target", "tests", "can_deserialize_and_reserialize"]
196 .iter()
197 .collect();
198 let mut invalid_diffs = Vec::new();
199
200 for entry in glob(FIXTURES_GLOB).expect("Failed to read glob pattern") {
201 let entry = entry.unwrap();
202 let path = entry.as_path();
203
204 println!("Testing if {:?} is deserializable", path);
205
206 let (api_filename, parsed_spec_json_str, spec_json_str) =
207 compare_spec_through_json(path, &save_path_base);
208
209 if parsed_spec_json_str != spec_json_str {
210 invalid_diffs.push((
211 api_filename,
212 parsed_spec_json_str.clone(),
213 spec_json_str.clone(),
214 ));
215 File::create(path.with_extension("parsed"))
216 .unwrap()
217 .write_all(parsed_spec_json_str.as_bytes())
218 .unwrap();
219 File::create(path.with_extension("pretty"))
220 .unwrap()
221 .write_all(spec_json_str.as_bytes())
222 .unwrap();
223 }
224 }
225
226 for invalid_diff in &invalid_diffs {
227 println!("File {} failed JSON comparison!", invalid_diff.0);
228 }
229 assert_eq!(invalid_diffs.len(), 0);
230 }
231}