#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod spec;
pub use self::spec::Spec;
pub type OpenApiV3Spec = spec::Spec;
#[cfg(all(test, feature = "yaml-spec"))]
pub(crate) fn from_path(
path: impl AsRef<std::path::Path>,
) -> std::io::Result<Result<OpenApiV3Spec, yaml_serde::Error>> {
let file = std::fs::File::open(path.as_ref())?;
Ok(from_reader(file))
}
#[cfg(all(test, feature = "yaml-spec"))]
pub(crate) fn from_reader(read: impl std::io::Read) -> Result<OpenApiV3Spec, yaml_serde::Error> {
yaml_serde::from_reader::<_, OpenApiV3Spec>(read)
}
#[cfg(feature = "yaml-spec")]
pub fn from_yaml(yaml: impl AsRef<str>) -> Result<OpenApiV3Spec, yaml_serde::Error> {
yaml_serde::from_str(yaml.as_ref())
}
pub fn from_json(json: impl AsRef<str>) -> Result<OpenApiV3Spec, serde_json::Error> {
serde_json::from_str(json.as_ref())
}
#[cfg(feature = "yaml-spec")]
pub fn to_yaml(spec: &OpenApiV3Spec) -> Result<String, yaml_serde::Error> {
yaml_serde::to_string(spec)
}
pub fn to_json(spec: &OpenApiV3Spec) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(spec)
}
#[cfg(all(test, feature = "yaml-spec"))]
mod tests {
use std::{
fs::{self, read_to_string, File},
io::Write,
path::{self, Path},
};
use pretty_assertions::assert_eq;
use super::*;
fn write_to_file<P>(path: P, filename: &str, data: &str)
where
P: AsRef<Path> + std::fmt::Debug,
{
println!(" Saving string to {path:?}...");
std::fs::create_dir_all(&path).unwrap();
let full_filename = path.as_ref().to_path_buf().join(filename);
let mut f = File::create(full_filename).unwrap();
f.write_all(data.as_bytes()).unwrap();
}
fn convert_yaml_str_to_json(yaml_str: &str) -> String {
let yaml: yaml_serde::Value = yaml_serde::from_str(yaml_str).unwrap();
let json: serde_json::Value = yaml_serde::from_value(yaml).unwrap();
serde_json::to_string_pretty(&json).unwrap()
}
fn compare_spec_through_json(
input_file: &Path,
save_path_base: &Path,
) -> (String, String, String) {
let spec_yaml_str = read_to_string(input_file)
.unwrap_or_else(|err| panic!("failed to read contents of {input_file:?}: {err}"));
let spec_json_str = convert_yaml_str_to_json(&spec_yaml_str);
let parsed_spec = from_path(input_file).unwrap().unwrap();
let parsed_spec_json = serde_json::to_value(parsed_spec).unwrap();
let parsed_spec_json_str: String = serde_json::to_string_pretty(&parsed_spec_json).unwrap();
let api_filename = input_file
.file_name()
.unwrap()
.to_str()
.unwrap()
.replace(".yaml", ".json");
let mut save_path = save_path_base.to_path_buf();
save_path.push("yaml_to_json");
write_to_file(&save_path, &api_filename, &spec_json_str);
let mut save_path = save_path_base.to_path_buf();
save_path.push("yaml_to_spec_to_json");
write_to_file(&save_path, &api_filename, &parsed_spec_json_str);
(api_filename, parsed_spec_json_str, spec_json_str)
}
#[test]
#[ignore = "lib does not support all schema attributes yet"]
fn test_serialization_round_trip() {
let save_path_base: path::PathBuf = ["target", "tests", "test_serialization_round_trip"]
.iter()
.collect();
for entry in fs::read_dir("data/oas-samples").unwrap() {
let entry = entry.unwrap();
let path = entry.path();
println!("Testing if {path:?} is deserializable");
let (api_filename, parsed_spec_json_str, spec_json_str) =
compare_spec_through_json(&path, &save_path_base);
assert_eq!(
parsed_spec_json_str.lines().collect::<Vec<_>>(),
spec_json_str.lines().collect::<Vec<_>>(),
"contents did not match for api {}",
api_filename
);
}
}
#[test]
fn test_json_from_reader() {
let yaml = r#"openapi: "3"
paths: {}
info:
title: Test API
version: "0.1"
components:
schemas:
assets:
title: Assets
type: array
items: { type: integer }"#;
let json = r#"{
"openapi": "3",
"paths": {},
"info": {
"title": "Test API",
"version": "0.1"
},
"components": {
"schemas": {
"assets": {
"title": "Assets",
"type": "array",
"items": {
"type": "integer"
}
}
}
}
}"#;
assert_eq!(
from_reader(json.as_bytes()).unwrap(),
from_reader(yaml.as_bytes()).unwrap()
);
}
}