From 9b4d0f9a226e360cd1fe21b61f52bb1d570f22c5 Mon Sep 17 00:00:00 2001 From: Jonathan Plasse Date: Sun, 24 Mar 2024 17:39:09 +0100 Subject: [PATCH] Add JSON schema generation as a hidden CLI command --- Cargo.toml | 3 +- src/bin/generate_json_schema.rs | 24 ------------- src/generate_json_schema.rs | 63 +++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/main.rs | 8 +++++ 5 files changed, 76 insertions(+), 25 deletions(-) delete mode 100644 src/bin/generate_json_schema.rs create mode 100644 src/generate_json_schema.rs diff --git a/Cargo.toml b/Cargo.toml index f1c57be20..b258b645a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,6 +132,7 @@ wild = { version = "2.1.0", optional = true } # JSON schema schemars = { version = "0.8.16", optional = true } +pretty_assertions = { version = "1.3.0", optional = true } [dev-dependencies] expect-test = "1.4.1" @@ -161,7 +162,7 @@ upload = [ "dep:dirs", ] -schemars = ["dep:schemars"] +schemars = ["dep:schemars", "dep:pretty_assertions"] # keyring doesn't support *BSD so it's not enabled in `full` by default password-storage = ["upload", "keyring"] diff --git a/src/bin/generate_json_schema.rs b/src/bin/generate_json_schema.rs deleted file mode 100644 index 05266d0d6..000000000 --- a/src/bin/generate_json_schema.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::fs; -use std::path::PathBuf; - -use anyhow::Result; -use schemars::schema_for; - -use maturin::pyproject_toml::ToolMaturin; - -pub(crate) fn main() -> Result<()> { - let schema = schema_for!(ToolMaturin); - let schema_string = serde_json::to_string_pretty(&schema).unwrap(); - let filename = "maturin.schema.json"; - let schema_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(filename); - - let current = fs::read_to_string(&schema_path)?; - if current == schema_string { - println!("Up-to-date: {filename}"); - } else { - println!("Updating: {filename}"); - fs::write(schema_path, schema_string.as_bytes())?; - } - - Ok(()) -} diff --git a/src/generate_json_schema.rs b/src/generate_json_schema.rs new file mode 100644 index 000000000..98df7213c --- /dev/null +++ b/src/generate_json_schema.rs @@ -0,0 +1,63 @@ +#![cfg(feature = "schemars")] +use std::fs; +use std::path::PathBuf; + +use anyhow::{bail, Result}; +use pretty_assertions::StrComparison; +use schemars::schema_for; + +use crate::pyproject_toml::ToolMaturin; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, clap::ValueEnum, Default)] +/// The mode to use when generating the JSON schema. +pub enum Mode { + /// Write the JSON schema to the file. + #[default] + Write, + /// Check if the JSON schema is up-to-date. + Check, + /// Print the JSON schema to stdout. + DryRun, +} + +/// Generate the JSON schema for the `pyproject.toml` file. +#[derive(Debug, clap::Parser)] +pub struct GenerateJsonSchemaOptions { + /// The mode to use when generating the JSON schema. + #[arg(long, default_value_t, value_enum)] + pub mode: Mode, +} + +/// Generate the JSON schema for the `pyproject.toml` file. +pub fn generate_json_schema(args: GenerateJsonSchemaOptions) -> Result<()> { + let schema = schema_for!(ToolMaturin); + let schema_string = serde_json::to_string_pretty(&schema).unwrap(); + let filename = "maturin.schema.json"; + let schema_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(filename); + + match args.mode { + Mode::DryRun => { + println!("{schema_string}"); + } + Mode::Check => { + let current = fs::read_to_string(schema_path)?; + if current == schema_string { + println!("Up-to-date: {filename}"); + } else { + let comparison = StrComparison::new(¤t, &schema_string); + bail!("{filename} changed, please run `cargo run --features schemars -- generate-json-schema`:\n{comparison}",); + } + } + Mode::Write => { + let current = fs::read_to_string(&schema_path)?; + if current == schema_string { + println!("Up-to-date: {filename}"); + } else { + println!("Updating: {filename}"); + fs::write(schema_path, schema_string.as_bytes())?; + } + } + } + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 7122b13de..0145753d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,8 @@ pub use crate::build_options::{BuildOptions, CargoOptions}; pub use crate::cargo_toml::CargoToml; pub use crate::compile::{compile, BuildArtifact}; pub use crate::develop::{develop, DevelopOptions}; +#[cfg(feature = "schemars")] +pub use crate::generate_json_schema::{generate_json_schema, GenerateJsonSchemaOptions, Mode}; pub use crate::metadata::{Metadata23, WheelMetadata}; pub use crate::module_writer::{ write_dist_info, ModuleWriter, PathWriter, SDistWriter, WheelWriter, @@ -51,6 +53,7 @@ pub mod ci; mod compile; mod cross_compile; mod develop; +mod generate_json_schema; mod metadata; mod module_writer; #[cfg(feature = "scaffolding")] diff --git a/src/main.rs b/src/main.rs index 2a8b1bbee..cdbec3cf3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,8 @@ use maturin::{ develop, write_dist_info, BridgeModel, BuildOptions, CargoOptions, DevelopOptions, PathWriter, PlatformTag, PythonInterpreter, Target, }; +#[cfg(feature = "schemars")] +use maturin::{generate_json_schema, GenerateJsonSchemaOptions}; #[cfg(feature = "upload")] use maturin::{upload_ui, PublishOpt}; use std::env; @@ -141,6 +143,10 @@ enum Opt { #[cfg(feature = "zig")] #[command(subcommand, hide = true)] Zig(Zig), + /// Generate the JSON schema for the `pyproject.toml` file. + #[cfg(feature = "schemars")] + #[command(name = "generate-json-schema", hide = true)] + GenerateJsonSchema(GenerateJsonSchemaOptions), } /// Backend for the PEP 517 integration. Not for human consumption @@ -425,6 +431,8 @@ fn run() -> Result<()> { .execute() .context("Failed to run zig linker wrapper")?; } + #[cfg(feature = "schemars")] + Opt::GenerateJsonSchema(args) => generate_json_schema(args)?, } Ok(())