use std::path::Path;
use std::{fs, io};
use thiserror::Error;
use typify::{TypeSpace, TypeSpaceSettings};
#[derive(Error, Debug)]
pub enum BuildError {
#[error("I/O error")]
IoError(#[from] io::Error),
#[error("JSON schema error")]
SchemaError(#[from] typify::Error),
#[error("Rust syntax error")]
SyntaxError(#[from] syn::Error),
#[error("JSON parsing error")]
JsonError(#[from] serde_json::Error),
#[error("other error")]
Other,
}
fn main() -> Result<(), BuildError> {
build(
"./src/csaf/csaf2_0/csaf_json_schema.json",
"csaf/csaf2_0/schema.rs",
false,
true,
)?;
build(
"./src/csaf/csaf2_1/ssvc-1-0-1-merged.schema.json",
"csaf/csaf2_1/ssvc_schema.rs",
false,
false,
)?;
build(
"./src/csaf/csaf2_1/csaf.json",
"csaf/csaf2_1/schema.rs",
false,
true,
)?;
build(
"ssvc/data/schema/v1/Decision_Point-1-0-1.schema.json",
"csaf/csaf2_1/ssvc_dp_schema.rs",
true,
false,
)?;
Ok(())
}
fn build(input: &str, output: &str, from_root: bool, no_date_time: bool) -> Result<(), BuildError> {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let input_path = if from_root {
let mut manifest_path = manifest_dir.to_path_buf();
if manifest_path.to_string_lossy().contains("target/package") {
for _ in 0..3 {
manifest_path.pop();
}
} else {
manifest_path.pop();
}
manifest_path.join(input)
} else {
manifest_dir.join(input)
};
let content = fs::read_to_string(&input_path)?;
let mut schema_value = serde_json::from_str(&content)?;
if no_date_time {
remove_datetime_formats(&mut schema_value);
}
let schema: schemars::schema::RootSchema = serde_json::from_value(schema_value)?;
let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
type_space.add_root_schema(schema)?;
let content = prettyplease::unparse(&syn::parse2::<syn::File>(type_space.to_stream())?);
let mut out_file = Path::new("src").to_path_buf();
out_file.push(output);
Ok(fs::write(out_file, content)?)
}
fn remove_datetime_formats(value: &mut serde_json::Value) {
if let serde_json::Value::Object(map) = value {
if let Some(format) = map.get("format") {
if format.as_str() == Some("date-time") {
map.remove("format");
}
}
for (_, v) in map.iter_mut() {
remove_datetime_formats(v);
}
} else if let serde_json::Value::Array(arr) = value {
for item in arr.iter_mut() {
remove_datetime_formats(item);
}
}
}