use std::{
fs::{self, File},
path::Path,
};
fn fix_enum_values(value: &mut serde_json::Value) {
match value {
serde_json::Value::Object(map) => {
if let Some(serde_json::Value::Array(ref mut values)) = map.get_mut("enum") {
for item in values.iter_mut() {
if let serde_json::Value::String(s) = item {
match s.as_str() {
">" => *s = "GreaterThan".to_string(),
">=" => *s = "GreaterThanOrEqual".to_string(),
"<" => *s = "LessThan".to_string(),
"<=" => *s = "LessThanOrEqual".to_string(),
"==" => *s = "Equal".to_string(),
_ => {}
}
}
}
}
for (_, v) in map.iter_mut() {
fix_enum_values(v);
}
}
serde_json::Value::Array(arr) => {
for item in arr.iter_mut() {
fix_enum_values(item);
}
}
_ => {}
}
}
fn simplify_single_ref_allof(value: &mut serde_json::Value) {
match value {
serde_json::Value::Object(map) => {
let should_simplify = if let Some(serde_json::Value::Array(arr)) = map.get("allOf") {
arr.len() == 1
&& arr[0].is_object()
&& arr[0].as_object().unwrap().contains_key("$ref")
&& map.keys().any(|k| {
matches!(
k.as_str(),
"pattern"
| "enum"
| "minLength"
| "maxLength"
| "minimum"
| "maximum"
| "format"
| "default"
)
})
} else {
false
};
if should_simplify {
let ref_value = map
.get("allOf")
.and_then(|v| v.as_array())
.and_then(|arr| arr[0].as_object())
.and_then(|obj| obj.get("$ref"))
.cloned();
if let Some(ref_val) = ref_value {
map.remove("allOf");
map.remove("pattern");
map.remove("enum");
map.remove("minLength");
map.remove("maxLength");
map.remove("minimum");
map.remove("maximum");
map.remove("format");
map.remove("default");
map.insert("$ref".to_string(), ref_val);
}
}
for (_, v) in map.iter_mut() {
simplify_single_ref_allof(v);
}
}
serde_json::Value::Array(arr) => {
for item in arr.iter_mut() {
simplify_single_ref_allof(item);
}
}
_ => {}
}
}
fn collapse_ref_with_scalar_allof(value: &mut serde_json::Value) {
match value {
serde_json::Value::Object(map) => {
let ref_value = map.get("allOf").and_then(|v| v.as_array()).and_then(|arr| {
let refs: Vec<_> = arr.iter().filter_map(|s| s.get("$ref")).collect();
let scalar_only = arr.iter().all(|s| {
s.get("$ref").is_some()
|| (s.get("properties").is_none()
&& s.get("allOf").is_none()
&& s.get("oneOf").is_none()
&& s.get("anyOf").is_none())
});
(arr.len() > 1 && refs.len() == 1 && scalar_only).then(|| refs[0].clone())
});
if let Some(ref_val) = ref_value {
map.remove("allOf");
map.insert("$ref".to_string(), ref_val);
}
for (_, v) in map.iter_mut() {
collapse_ref_with_scalar_allof(v);
}
}
serde_json::Value::Array(arr) => {
for item in arr.iter_mut() {
collapse_ref_with_scalar_allof(item);
}
}
_ => {}
}
}
fn remove_not_schemas(value: &mut serde_json::Value) {
match value {
serde_json::Value::Object(map) => {
map.remove("not");
for (_, v) in map.iter_mut() {
remove_not_schemas(v);
}
}
serde_json::Value::Array(arr) => {
for item in arr.iter_mut() {
remove_not_schemas(item);
}
}
_ => {}
}
}
fn main() {
if std::env::var("CDP_GENERATE").is_err() {
println!("cargo:warning=Skipping code generation. Set CDP_GENERATE=1 to generate code.");
println!("cargo:warning=Run 'make generate' to generate api.rs from openapi.yaml");
return;
}
let src = "../openapi.yaml";
println!("cargo:rerun-if-changed={}", src);
let file = File::open(src).unwrap();
let mut json: serde_json::Value = serde_yaml::from_reader(file).unwrap();
fix_enum_values(&mut json);
simplify_single_ref_allof(&mut json);
collapse_ref_with_scalar_allof(&mut json);
remove_not_schemas(&mut json);
let spec = serde_json::from_str(&serde_json::to_string_pretty(&json).unwrap()).unwrap();
let mut settings = progenitor_middleware::GenerationSettings::default();
settings.with_interface(progenitor_middleware::InterfaceStyle::Builder);
let mut generator = progenitor_middleware::Generator::new(&settings);
let tokens = generator.generate_tokens(&spec).unwrap();
let ast = syn::parse2(tokens).unwrap();
let content = prettyplease::unparse(&ast);
let mut out_file = Path::new("./src/").to_path_buf();
out_file.push("api.rs");
fs::write(out_file, content).unwrap();
println!("cargo:warning=Successfully generated api.rs from openapi.yaml");
}