diff --git a/.github/workflows/test-serde.yml b/.github/workflows/test-serde.yml index 164354a..e658bfa 100644 --- a/.github/workflows/test-serde.yml +++ b/.github/workflows/test-serde.yml @@ -14,3 +14,4 @@ jobs: cargo test --no-default-features --features=serde cargo test --no-default-features --features=serde,std cargo test --no-default-features --features=schemars + cargo test --no-default-features --features=schemars1 diff --git a/CHANGELOG.md b/CHANGELOG.md index ef55f03..d1a5017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - Added optional `bytecheck` support. Enable using the `bytecheck` feature. +- Added optional `schemars` v1 support. Enable using the `schemars1` feature. ### Fixed diff --git a/Cargo.toml b/Cargo.toml index e24a115..d148052 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ serde = ["dep:serde"] borsh = ["dep:borsh"] schemars = ["dep:schemars", "std"] +schemars1 = ["dep:schemars1", "std"] # Derive/implement traits for bytecheck bytecheck = ["dep:bytecheck"] @@ -53,6 +54,7 @@ defmt = { version = "1", optional = true } serde = { version = "1.0", optional = true, default-features = false } borsh = { version = "1.5.1", optional = true, features = ["unstable__schema"], default-features = false } schemars = { version = "0.8.21", optional = true, features = ["derive"], default-features = false } +schemars1 = { package = "schemars", version = "1", optional = true, default-features = false } bytemuck = { version = "1", optional = true, default-features = false } bin-proto = { version = "0.12.2", optional = true, default-features = false } bytecheck = { version = "0.8.2", optional = true, default-features = false } @@ -62,3 +64,4 @@ quickcheck = { version = "1", optional = true, default-features = false } [dev-dependencies] serde_test = "1.0" +serde_json = "1.0" diff --git a/src/common.rs b/src/common.rs index 1d6352e..23ed850 100644 --- a/src/common.rs +++ b/src/common.rs @@ -290,6 +290,45 @@ macro_rules! impl_schemars { Schema::Object(schema_object) } } + + #[cfg(feature = "schemars1")] + impl schemars1::JsonSchema for $type + where + Self: Integer, + { + fn inline_schema() -> bool { + true + } + + fn schema_name() -> alloc::borrow::Cow<'static, str> { + use alloc::string::ToString; + [$str_prefix, &BITS.to_string()].concat().into() + } + + fn json_schema(_gen: &mut schemars1::SchemaGenerator) -> schemars1::Schema { + // The `schemars` crate only provides the `minimum` and `maximum` fields for "small" + // integers (i8/u8/i16/u16). + // + // The rationale for this choice is explained in this issue: + // https://github.com/GREsau/schemars/issues/298 + // + // We mimic that behavior and only provide the `minimum` and `maximum` fields for + // integers smaller than 32 bits. + if BITS < 32 { + schemars1::json_schema!({ + "type": "integer", + "format": Self::schema_name(), + "minimum": Self::MIN.as_i64(), + "maximum": Self::MAX.as_i64(), + }) + } else { + schemars1::json_schema!({ + "type": "integer", + "format": Self::schema_name(), + }) + } + } + } }; } diff --git a/tests/tests.rs b/tests/tests.rs index 9c5eedb..36f4003 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -4278,6 +4278,112 @@ fn schemars_signed() { assert_eq!(i8, i9); } +#[cfg(feature = "schemars1")] +#[test] +fn schemars1_unsigned() { + use schemars1::{generate::SchemaSettings, schema_for}; + use serde_json::json; + + let meta = SchemaSettings::default().meta_schema.unwrap(); + + assert_eq!( + schema_for!(u1).to_value(), + json!({ + "$schema": meta, + "type": "integer", + "format": "uint1", + "title": "uint1", + "minimum": 0, + "maximum": 1, + }) + ); + + assert_eq!( + schema_for!(u31).to_value(), + json!({ + "$schema": meta, + "type": "integer", + "format": "uint31", + "title": "uint31", + "minimum": 0, + "maximum": 0x7fffffff, + }) + ); + + assert_eq!( + schema_for!(u33).to_value(), + json!({ + "$schema": meta, + "type": "integer", + "format": "uint33", + "title": "uint33" + }) + ); + + assert_eq!( + schema_for!(u127).to_value(), + json!({ + "$schema": meta, + "type": "integer", + "format": "uint127", + "title": "uint127" + }) + ); +} + +#[cfg(feature = "schemars1")] +#[test] +fn schemars1_signed() { + use schemars1::{generate::SchemaSettings, schema_for}; + use serde_json::json; + + let meta = SchemaSettings::default().meta_schema.unwrap(); + + assert_eq!( + schema_for!(i1).to_value(), + json!({ + "$schema": meta, + "type": "integer", + "format": "int1", + "title": "int1", + "minimum": -1, + "maximum": 0, + }) + ); + + assert_eq!( + schema_for!(i31).to_value(), + json!({ + "$schema": meta, + "type": "integer", + "format": "int31", + "title": "int31", + "minimum": -0x40000000, + "maximum": 0x3fffffff, + }) + ); + + assert_eq!( + schema_for!(i33).to_value(), + json!({ + "$schema": meta, + "type": "integer", + "format": "int33", + "title": "int33" + }) + ); + + assert_eq!( + schema_for!(i127).to_value(), + json!({ + "$schema": meta, + "type": "integer", + "format": "int127", + "title": "int127" + }) + ); +} + #[cfg(feature = "bytemuck")] mod bytemuck { use arbitrary_int::prelude::*;