diff --git a/Cargo.lock b/Cargo.lock index 30bc80c8..b6b9a0a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -405,6 +405,17 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jiff" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0ce60560149333a8e41ca7dc78799c47c5fd435e2bc18faf6a054382eec037" +dependencies = [ + "portable-atomic", + "portable-atomic-util", + "serde", +] + [[package]] name = "js-sys" version = "0.3.70" @@ -630,6 +641,21 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -781,6 +807,7 @@ dependencies = [ "either", "garde", "indexmap", + "jiff", "jsonschema", "pretty_assertions", "ref-cast", diff --git a/README.md b/README.md index fc68efc7..3e55e2dc 100644 --- a/README.md +++ b/README.md @@ -269,6 +269,7 @@ Schemars can implement `JsonSchema` on types from several popular crates, enable - `chrono04` - [chrono](https://crates.io/crates/chrono) (^0.4) - `either1` - [either](https://crates.io/crates/either) (^1.3) - `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0) +- `jiff01` - [jiff](https://crates.io/crates/jiff) (^0.1) - `rust_decimal1` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0) - `semver1` - [semver](https://crates.io/crates/semver) (^1.0.9) - `smallvec1` - [smallvec](https://crates.io/crates/smallvec) (^1.0) diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml index ca7ffbbc..ad8a4f06 100644 --- a/schemars/Cargo.toml +++ b/schemars/Cargo.toml @@ -26,6 +26,7 @@ bytes1 = { version = "1.0", default-features = false, optional = true, package = chrono04 = { version = "0.4", default-features = false, optional = true, package = "chrono" } either1 = { version = "1.3", default-features = false, optional = true, package = "either" } indexmap2 = { version = "2.0", default-features = false, optional = true, package = "indexmap" } +jiff01 = { version = "0.1", default-features = false, optional = true, package = "jiff" } rust_decimal1 = { version = "1", default-features = false, optional = true, package = "rust_decimal" } semver1 = { version = "1.0.9", default-features = false, optional = true, package = "semver" } smallvec1 = { version = "1.0", default-features = false, optional = true, package = "smallvec" } @@ -51,6 +52,7 @@ bytes1 = { version = "1.0", default-features = false, features = ["serde"], pack chrono04 = { version = "0.4", default-features = false, features = ["serde"], package = "chrono" } either1 = { version = "1.3", default-features = false, features = ["serde"], package = "either" } indexmap2 = { version = "2.0", default-features = false, features = ["serde"], package = "indexmap" } +jiff01 = { version = "0.1", default-features = false, features = ["serde"], package = "jiff" } rust_decimal1 = { version = "1", default-features = false, features = ["serde"], package = "rust_decimal" } semver1 = { version = "1.0.9", default-features = false, features = ["serde"], package = "semver" } smallvec1 = { version = "1.0", default-features = false, features = ["serde"], package = "smallvec" } diff --git a/schemars/src/json_schema_impls/jiff01.rs b/schemars/src/json_schema_impls/jiff01.rs new file mode 100644 index 00000000..ad1f60aa --- /dev/null +++ b/schemars/src/json_schema_impls/jiff01.rs @@ -0,0 +1,37 @@ +use crate::{json_schema, JsonSchema, Schema, SchemaGenerator}; +use alloc::borrow::Cow; +use jiff01::civil::{Date, DateTime, Time}; +use jiff01::{SignedDuration, Timestamp, Zoned}; + +macro_rules! formatted_string_impl { + ($ty:ident, $format:literal) => { + formatted_string_impl!($ty, $format, JsonSchema for $ty); + }; + ($ty:ident, $format:literal, $($desc:tt)+) => { + impl $($desc)+ { + always_inline!(); + + fn schema_name() -> Cow<'static, str> { + stringify!($ty).into() + } + + fn schema_id() -> Cow<'static, str> { + stringify!(jiff::$ty).into() + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + json_schema!({ + "type": "string", + "format": $format + }) + } + } + }; +} + +formatted_string_impl!(SignedDuration, "duration"); +formatted_string_impl!(Timestamp, "date-time"); +formatted_string_impl!(Zoned, "date-time"); +formatted_string_impl!(Date, "date"); +formatted_string_impl!(Time, "partial-time"); +formatted_string_impl!(DateTime, "partial-date-time"); diff --git a/schemars/src/json_schema_impls/mod.rs b/schemars/src/json_schema_impls/mod.rs index cbe1a075..53b08dd9 100644 --- a/schemars/src/json_schema_impls/mod.rs +++ b/schemars/src/json_schema_impls/mod.rs @@ -74,6 +74,9 @@ mod either1; #[cfg(feature = "indexmap2")] mod indexmap2; +#[cfg(feature = "jiff01")] +mod jiff01; + #[cfg(feature = "semver1")] mod semver1; diff --git a/schemars/tests/integration/jiff.rs b/schemars/tests/integration/jiff.rs new file mode 100644 index 00000000..e2db56c3 --- /dev/null +++ b/schemars/tests/integration/jiff.rs @@ -0,0 +1,43 @@ +use crate::prelude::*; +use jiff01::civil::{Date, DateTime, Time}; +use jiff01::{Timestamp, Zoned}; + +#[derive(JsonSchema, Serialize, Deserialize)] +struct JiffTypes { + date_time_ts: Timestamp, + date_time_zoned: Zoned, + naive_date: Date, + naive_date_time: DateTime, + naive_time: Time, +} + +#[test] +fn jiff() { + test!(JiffTypes).assert_snapshot(); + + test!(Timestamp) + .assert_allows_ser_roundtrip_default() + .assert_matches_de_roundtrip(arbitrary_values()); + + // test!(Zoned) + // .assert_allows_ser_roundtrip_default() + // .assert_matches_de_roundtrip(arbitrary_values()); + + test!(Date) + .assert_allows_ser_roundtrip_default() + .assert_matches_de_roundtrip(arbitrary_values()); + + test!(DateTime) + .assert_allows_ser_roundtrip_default() + .assert_matches_de_roundtrip(arbitrary_values_except( + Value::is_string, + "Custom format 'partial-date-time', so arbitrary strings technically allowed by schema", + )); + + test!(Time) + .assert_allows_ser_roundtrip_default() + .assert_matches_de_roundtrip(arbitrary_values_except( + Value::is_string, + "Custom format 'date-time', so arbitrary strings technically allowed by schema", + )); +} diff --git a/schemars/tests/integration/main.rs b/schemars/tests/integration/main.rs index eb445268..8417102e 100644 --- a/schemars/tests/integration/main.rs +++ b/schemars/tests/integration/main.rs @@ -28,6 +28,8 @@ mod garde; #[cfg(feature = "indexmap2")] mod indexmap; mod inline_subschemas; +#[cfg(feature = "jiff01")] +mod jiff; mod macros; mod remote_derive; mod same_name; diff --git a/schemars/tests/integration/snapshots/schemars/tests/integration/jiff.rs~jiff.json b/schemars/tests/integration/snapshots/schemars/tests/integration/jiff.rs~jiff.json new file mode 100644 index 00000000..3a089e82 --- /dev/null +++ b/schemars/tests/integration/snapshots/schemars/tests/integration/jiff.rs~jiff.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "JiffTypes", + "type": "object", + "properties": { + "date_time_ts": { + "type": "string", + "format": "date-time" + }, + "date_time_zoned": { + "type": "string", + "format": "date-time" + }, + "naive_date": { + "type": "string", + "format": "date" + }, + "naive_date_time": { + "type": "string", + "format": "partial-date-time" + }, + "naive_time": { + "type": "string", + "format": "partial-time" + } + }, + "required": [ + "date_time_ts", + "date_time_zoned", + "naive_date", + "naive_date_time", + "naive_time" + ] +} \ No newline at end of file