use crate::pathgen;
use crate::structgen;
use openapiv3::Schema;
use openapiv3::SchemaKind;
use openapiv3::Type;
use openapiv3::{OpenAPI, ReferenceOr};
use std::{
fs::{self, File},
io::{self, Write},
path::Path,
};
pub fn gen(input_path: impl AsRef<Path>, output_path: impl AsRef<Path>, workaround_mode: bool) {
let input = fs::read_to_string(input_path).unwrap();
let api: OpenAPI = serde_yaml::from_str(&input).unwrap();
let output_path = output_path.as_ref();
create_lib_dir(output_path).unwrap();
let mut types_file = File::create(output_path.join("src/").join("types.rs")).unwrap();
for (name, schema) in &api.components.unwrap().schemas {
let s = match schema {
ReferenceOr::Item(x) => x,
_ => continue,
};
if let Some(structure) = structgen::gen(name, s, workaround_mode) {
types_file.write_all(structure.as_bytes()).unwrap();
}
}
let mut paths_file = File::create(output_path.join("src/").join("paths.rs")).unwrap();
write!(paths_file, "{}", include_str!("templates/usings.template")).unwrap();
for (name, path) in &api.paths.paths {
let p = match path {
ReferenceOr::Item(x) => x,
_ => continue,
};
if let Some(paths) = pathgen::gen(name, p) {
paths_file.write_all(paths.as_bytes()).unwrap();
}
}
}
fn create_lib_dir(output_path: &Path) -> io::Result<()> {
println!("Starting repackaging into crate...");
_ = fs::create_dir(output_path);
let src_dir = output_path.join("src/");
fs::create_dir_all(&src_dir)?;
fs::write(
&src_dir.join("util.rs"),
include_str!("templates/util.rs.template"),
)?;
fs::write(
&src_dir.join("lib.rs"),
include_str!("templates/lib.rs.template"),
)?;
let mut cargo_file = fs::File::create(output_path.join("Cargo.toml"))?;
write!(
cargo_file,
include_str!("templates/Cargo.toml.template"),
output_path.file_name().unwrap().to_string_lossy()
)?;
fs::write(
output_path.join("build.rs"),
include_str!("templates/build.rs.template"),
)?;
fs::write(
output_path.join("README.md"),
include_str!("templates/README.md.template"),
)?;
println!("Output successfully repackaged!");
Ok(())
}
pub fn make_comment(input: Option<String>, indent: usize) -> String {
match input {
Some(x) => x
.split('\n')
.map(|x| format!("{}/// {}\n", "\t".repeat(indent), x))
.collect::<Vec<_>>()
.concat(),
None => String::new(),
}
}
pub fn type_to_string(ty: &ReferenceOr<Schema>) -> String {
match ty {
ReferenceOr::Reference { reference } => reference.replace("#/components/schemas/", ""),
ReferenceOr::Item(item) => {
let mut base = match &item.schema_kind {
SchemaKind::Type(t) => match t {
Type::String(_) => "String".to_owned(),
Type::Number(_) => "f64".to_owned(),
Type::Integer(int) => {
let signed = match int.minimum {
Some(x) => x < 0,
None => false,
};
let int_size = match int.maximum {
Some(x) => {
if signed {
if x <= i8::MAX.into() {
"i8"
} else if x <= i16::MAX.into() {
"i16"
} else if x <= i32::MAX.into() {
"i32"
} else {
"i64"
}
} else {
if x <= u8::MAX.into() {
"u8"
} else if x <= u16::MAX.into() {
"u16"
} else if x <= u32::MAX.into() {
"u32"
} else {
"u64"
}
}
}
None => "i64",
};
int_size.to_owned()
}
Type::Object(_) => {
"Option<std::collections::HashMap<String, serde_json::Value>>".to_owned()
}
Type::Boolean(_) => "bool".to_owned(),
Type::Array(x) => {
let items = x.items.as_ref().unwrap().clone().unbox();
format!("Vec<{}>", type_to_string(&items))
}
},
SchemaKind::AllOf { all_of } => {
format!("Option<{}>", type_to_string(&all_of[0].clone()))
}
_ => "serde_json::Value".to_owned(),
};
if item.schema_data.nullable && !base.contains("Option<") {
base = format!("Option<{}>", base);
}
base
}
}
}