fn main() {
#[cfg(not(feature = "dsl-schema"))]
panic!("this tool requires the `dsl-schema` feature");
#[cfg(feature = "dsl-schema")]
{
impls::run();
}
}
#[cfg(feature = "dsl-schema")]
mod impls {
use std::fs::File;
use std::io::Write;
use std::path::Path;
use polars_plan::dsl::DslPlan;
use schemars::Schema;
use sha2::Digest;
const DEFAULT_HASHES_PATH: &str = "crates/polars-plan/dsl-schema-hashes.json";
pub fn run() {
let mut args = std::env::args();
let _ = args.next();
let cmd = args
.next()
.expect("missing command [generate, update-hashes, check-hashes]");
let path = args.next();
if let Some(unknown) = args.next() {
panic!("unknown argument: `{unknown}`");
}
match cmd.as_str() {
"generate" => {
generate(path.unwrap_or("./dsl-schema.json".to_owned()));
},
"update-hashes" => {
update_hashes(path.unwrap_or(DEFAULT_HASHES_PATH.to_owned()));
},
"check-hashes" => {
check_hashes(path.unwrap_or(DEFAULT_HASHES_PATH.to_owned()));
},
unknown => {
panic!("unknown command: `{unknown}`");
},
}
}
fn generate(path: impl AsRef<Path>) {
let schema = DslPlan::dsl_schema();
let mut file = File::create(path).expect("failed to open the schema file for writing");
serde_json::to_writer_pretty(&mut file, &schema).expect("failed to serialize the schema");
writeln!(&mut file).expect("failed to write the last newline");
file.flush().expect("failed to flush the schema file");
}
fn update_hashes(path: impl AsRef<Path>) {
std::fs::write(path, current_schema_hashes())
.expect("failed to write the schema into the file");
eprintln!("the DSL schema file was updated");
}
fn check_hashes(path: impl AsRef<Path>) {
let file_hashes =
std::fs::read_to_string(path).expect("failed to read the schema hashes from the file");
let expected = current_schema_hashes();
if file_hashes != expected {
eprintln!("the schema hashes are not up to date, run `make update-dsl-schema-hashes`");
eprintln!("expected schema hashes:");
eprintln!("{}", expected);
std::process::exit(1);
}
eprintln!("the DSL schema hashes are up to date");
}
fn current_schema_hashes() -> String {
let schema = DslPlan::dsl_schema();
let mut hashes = serde_json::Map::new();
if let Some(definitions) = schema.get("$defs") {
if let Some(definitions) = definitions.as_object() {
for (name, def) in definitions {
let mut def = def.to_owned();
def.sort_all_objects();
let schema: &Schema = (&def).try_into().unwrap();
hashes.insert(name.into(), schema_hash(schema).into());
}
assert!(definitions.contains_key("DslPlan"));
}
}
hashes.sort_keys();
serde_json::to_string_pretty(&hashes).expect("failed to serialize schema hashes file")
}
fn schema_hash(schema: &Schema) -> String {
let mut digest = sha2::Sha256::new();
serde_json::to_writer(&mut digest, schema).expect("failed to serialize the schema");
let hash = digest.finalize();
format!("{hash:064x}")
}
}