Add JSON Schema support (#3046)

## Summary

This PR adds JSON Schema support. The setup mirrors Ruff's own.
This commit is contained in:
Charlie Marsh 2024-04-17 13:24:41 -04:00 committed by GitHub
parent 7c5b13c412
commit 7fb2bf816f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 818 additions and 26 deletions

View file

@ -33,6 +33,7 @@ uv-normalize = { workspace = true }
uv-resolver = { workspace = true }
uv-toolchain = { workspace = true }
uv-types = { workspace = true }
uv-workspace = { workspace = true, features = ["schemars"] }
# Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace
# dependencies, to ensure that we're forced to think twice before including them in other crates.
@ -47,8 +48,10 @@ itertools = { workspace = true }
owo-colors = { workspace = true }
petgraph = { workspace = true }
poloto = { version = "19.1.2" }
pretty_assertions = { version = "1.4.0" }
resvg = { version = "0.29.0" }
rustc-hash = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tagu = { version = "0.1.6" }

View file

@ -0,0 +1,99 @@
use std::path::PathBuf;
use anstream::println;
use anyhow::{bail, Result};
use pretty_assertions::StrComparison;
use schemars::schema_for;
use uv_workspace::Options;
use crate::ROOT_DIR;
#[derive(clap::Args)]
pub(crate) struct GenerateJsonSchemaArgs {
/// Write the generated table to stdout (rather than to `uv.schema.json`).
#[arg(long, default_value_t, value_enum)]
mode: Mode,
}
#[derive(Copy, Clone, PartialEq, Eq, clap::ValueEnum, Default)]
enum Mode {
/// Update the content in the `configuration.md`.
#[default]
Write,
/// Don't write to the file, check if the file is up-to-date and error if not.
Check,
/// Write the generated help to stdout.
DryRun,
}
pub(crate) fn main(args: &GenerateJsonSchemaArgs) -> Result<()> {
let schema = schema_for!(Options);
let schema_string = serde_json::to_string_pretty(&schema).unwrap();
let filename = "uv.schema.json";
let schema_path = PathBuf::from(ROOT_DIR).join(filename);
match args.mode {
Mode::DryRun => {
println!("{schema_string}");
}
Mode::Check => match fs_err::read_to_string(schema_path) {
Ok(current) => {
if current == schema_string {
println!("Up-to-date: {filename}");
} else {
let comparison = StrComparison::new(&current, &schema_string);
bail!("{filename} changed, please run `cargo dev generate-json-schema`:\n{comparison}");
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
bail!("{filename} not found, please run `cargo dev generate-json-schema`");
}
Err(err) => {
bail!("{filename} changed, please run `cargo dev generate-json-schema`:\n{err}");
}
},
Mode::Write => match fs_err::read_to_string(&schema_path) {
Ok(current) => {
if current == schema_string {
println!("Up-to-date: {filename}");
} else {
println!("Updating: {filename}");
fs_err::write(schema_path, schema_string.as_bytes())?;
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
println!("Updating: {filename}");
fs_err::write(schema_path, schema_string.as_bytes())?;
}
Err(err) => {
bail!("{filename} changed, please run `cargo dev generate-json-schema`:\n{err}");
}
},
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::env;
use anyhow::Result;
use crate::generate_json_schema::Mode;
use super::{main, GenerateJsonSchemaArgs};
#[test]
fn test_generate_json_schema() -> Result<()> {
let mode = if env::var("UV_UPDATE_SCHEMA").as_deref() == Ok("1") {
Mode::Write
} else {
Mode::Check
};
main(&GenerateJsonSchemaArgs { mode })
}
}

View file

@ -22,6 +22,7 @@ use crate::build::{build, BuildArgs};
use crate::clear_compile::ClearCompileArgs;
use crate::compile::CompileArgs;
use crate::fetch_python::FetchPythonArgs;
use crate::generate_json_schema::GenerateJsonSchemaArgs;
use crate::render_benchmarks::RenderBenchmarksArgs;
use crate::resolve_cli::ResolveCliArgs;
use crate::wheel_metadata::WheelMetadataArgs;
@ -46,11 +47,14 @@ mod build;
mod clear_compile;
mod compile;
mod fetch_python;
mod generate_json_schema;
mod render_benchmarks;
mod resolve_cli;
mod resolve_many;
mod wheel_metadata;
const ROOT_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../");
#[derive(Parser)]
enum Cli {
/// Build a source distribution into a wheel
@ -76,6 +80,8 @@ enum Cli {
ClearCompile(ClearCompileArgs),
/// Fetch Python versions for testing
FetchPython(FetchPythonArgs),
/// Generate JSON schema for the TOML configuration file.
GenerateJSONSchema(GenerateJsonSchemaArgs),
}
#[instrument] // Anchor span to check for overhead
@ -97,6 +103,7 @@ async fn run() -> Result<()> {
Cli::Compile(args) => compile::compile(args).await?,
Cli::ClearCompile(args) => clear_compile::clear_compile(&args)?,
Cli::FetchPython(args) => fetch_python::fetch_python(args).await?,
Cli::GenerateJSONSchema(args) => generate_json_schema::main(&args)?,
}
Ok(())
}