diff --git a/Cargo.lock b/Cargo.lock index a66582eb..ea925f35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,35 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -20,6 +40,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "autocfg" version = "1.1.0" @@ -34,19 +60,34 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "basic-toml" -version = "0.1.1" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e819b667739967cd44d308b8c7b71305d8bb0729ac44a248aa08f33d01950b4" +checksum = "2f2139706359229bfa8f19142ac1155b4b80beafb7a60471ac5dd109d4a19778" dependencies = [ "serde", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -55,21 +96,30 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytecount" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -85,87 +135,33 @@ checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.41", ] [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ + "android-tzdata", "iana-time-zone", - "num-integer", "num-traits", "serde", - "winapi", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", + "windows-targets", ] [[package]] name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cxx" -version = "1.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.15", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.93" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "darling" -version = "0.20.0" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c99d16b88c92aef47e58dadd53e87b4bd234c29934947a6cec8b466300f99b" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -173,27 +169,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.0" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea05d2fcb27b53f7a98faddaf5f2914760330ab7703adfc9df13332b42189f9" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.15", + "syn 2.0.41", ] [[package]] name = "darling_macro" -version = "0.20.0" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bfb82b62b1b8a2a9808fb4caf844ede819a76cfc23b2827d7f94eefb49551eb" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.15", + "syn 2.0.41", ] [[package]] @@ -204,9 +200,9 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "dissimilar" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" +checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" [[package]] name = "doc-comment" @@ -223,6 +219,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + [[package]] name = "equivalent" version = "1.0.1" @@ -231,14 +233,24 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "expect-test" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d4661aca38d826eb7c72fe128e4238220616de4c0cc00db7bfc38e2e1364dd3" +checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" dependencies = [ "dissimilar", "once_cell", ] +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + [[package]] name = "fnv" version = "1.0.7" @@ -247,13 +259,36 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3027ae1df8d41b4bed2241c8fdad4acc1e7af60c8e17743534b545e77182d678" +dependencies = [ + "lazy_static", + "num", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + [[package]] name = "glob" version = "0.3.1" @@ -268,9 +303,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "serde", ] @@ -283,26 +318,25 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -313,9 +347,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -323,9 +357,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -334,44 +368,78 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "serde", ] +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", +] + [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] [[package]] -name = "libc" -version = "0.2.139" +name = "jsonschema" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978" +dependencies = [ + "ahash", + "anyhow", + "base64 0.21.5", + "bytecount", + "fancy-regex", + "fraction", + "getrandom", + "iso8601", + "itoa", + "memchr", + "num-cmp", + "once_cell", + "parking_lot", + "percent-encoding", + "regex", + "serde", + "serde_json", + "time", + "url", + "uuid", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "libc" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "litrs" @@ -380,25 +448,88 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9275e0933cf8bb20f008924c0cb07a0692fe54d8064996520bf998de9eb79aa" [[package]] -name = "log" -version = "0.4.17" +name = "lock_api" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ - "cfg-if", + "autocfg", + "scopeguard", ] +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] [[package]] name = "num-integer" @@ -410,32 +541,78 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] [[package]] name = "paste" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pretty_assertions" @@ -449,9 +626,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -469,18 +646,27 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" -version = "1.9.1" +version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", @@ -490,9 +676,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", @@ -501,15 +687,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rmp" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" dependencies = [ "byteorder", "num-traits", @@ -518,9 +704,9 @@ dependencies = [ [[package]] name = "rmp-serde" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b13be192e0220b8afb7222aa5813cb62cc269ebb5cac346ca6487681d2913e" +checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" dependencies = [ "byteorder", "rmp", @@ -540,33 +726,57 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] [[package]] -name = "scratch" -version = "1.0.3" +name = "schemars_derive" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.16" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -585,22 +795,33 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "indexmap 1.9.2", + "indexmap 2.1.0", "itoa", "ryu", "serde", @@ -608,9 +829,9 @@ dependencies = [ [[package]] name = "serde_test" -version = "1.0.152" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3611210d2d67e3513204742004d6ac6f589e521861dabb0f649b070eea8bed9e" +checksum = "5a2f49ace1498612d14f7e0b8245519584db8299541dfe31a06374a828d620ab" dependencies = [ "serde", ] @@ -619,23 +840,25 @@ dependencies = [ name = "serde_with" version = "3.4.0" dependencies = [ - "base64 0.21.0", + "base64 0.21.5", "chrono", "doc-comment", "document-features", "expect-test", "fnv", "glob", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "hex", - "indexmap 1.9.2", - "indexmap 2.0.0", + "indexmap 1.9.3", + "indexmap 2.1.0", + "jsonschema", "mime", "pretty_assertions", "regex", "rmp-serde", "ron", "rustversion", + "schemars", "serde", "serde-xml-rs", "serde_json", @@ -659,7 +882,7 @@ dependencies = [ "rustversion", "serde", "serde_json", - "syn 2.0.15", + "syn 2.0.41", "trybuild", "version-sync", ] @@ -676,17 +899,23 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.17" +version = "0.9.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" dependencies = [ - "indexmap 1.9.2", + "indexmap 2.1.0", "itoa", "ryu", "serde", "unsafe-libyaml", ] +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + [[package]] name = "strsim" version = "0.10.0" @@ -695,9 +924,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -706,9 +935,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" dependencies = [ "proc-macro2", "quote", @@ -717,38 +946,38 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.41", ] [[package]] name = "time" -version = "0.3.17" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ "itoa", "serde", @@ -764,9 +993,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" dependencies = [ "time-core", ] @@ -797,9 +1026,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.80" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501dbdbb99861e4ab6b60eb6a7493956a9defb644fd034bc4a5ef27c693c8a3a" +checksum = "196a58260a906cedb9bf6d8034b6379d0c11f552416960452f267402ceeddff1" dependencies = [ "basic-toml", "glob", @@ -812,24 +1041,24 @@ dependencies = [ [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -840,12 +1069,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - [[package]] name = "unsafe-libyaml" version = "0.2.10" @@ -854,15 +1077,21 @@ checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "url" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" + [[package]] name = "version-sync" version = "0.9.4" @@ -873,7 +1102,7 @@ dependencies = [ "pulldown-cmark", "regex", "semver", - "syn 1.0.107", + "syn 1.0.109", "toml", "url", ] @@ -884,11 +1113,17 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -896,24 +1131,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.41", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -921,22 +1156,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.41", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "winapi" @@ -956,9 +1191,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -969,6 +1204,72 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "xml-rs" version = "0.8.14" @@ -980,3 +1281,23 @@ name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zerocopy" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] diff --git a/serde_with/Cargo.toml b/serde_with/Cargo.toml index 5db0f798..a5fa23d8 100644 --- a/serde_with/Cargo.toml +++ b/serde_with/Cargo.toml @@ -111,6 +111,13 @@ macros = ["dep:serde_with_macros"] ## This pulls in `time` v0.3 as a dependency. ## Some functionality is only available when `alloc` or `std` is enabled too. time_0_3 = ["dep:time_0_3"] +## This feature enables integration with `schemars` 0.8. +## This makes `#[derive(JsonSchema)]` pick up the correct schema for the type +## used within `#[serde_as(as = ...)]`. +## +## This pulls in `schemars` v0.8 as a dependency. It will also implicitly enable +## the `std` feature as `schemars` is not `#[no_std]`. +schemars_0_8 = ["dep:schemars_0_8", "std", "serde_with_macros?/schemars_0_8"] # When adding new optional dependencies update the documentation in feature-flags.md [dependencies] @@ -128,17 +135,20 @@ serde = {version = "1.0.152", default-features = false, features = ["derive"] } serde_json = {version = "1.0.45", optional = true, default-features = false} serde_with_macros = {path = "../serde_with_macros", version = "=3.4.0", optional = true} time_0_3 = {package = "time", version = "~0.3.11", optional = true, default-features = false} +schemars_0_8 = {package = "schemars", version = "0.8.16", optional = true, default-features = false} [dev-dependencies] expect-test = "1.3.0" fnv = "1.0.6" glob = "0.3.0" +jsonschema = { version = "0.17.1", default-features = false, features = ["resolve-file"] } mime = "0.3.16" pretty_assertions = "1.4.0" regex = {version = "1.9.1", default-features = false, features = ["std"]} rmp-serde = "1.1.0" ron = "0.8" rustversion = "1.0.0" +schemars_0_8 = {package = "schemars", version = "0.8.16"} serde_json = {version = "1.0.25", features = ["preserve_order"]} serde_test = "1.0.124" serde_yaml = "0.9.2" @@ -210,6 +220,11 @@ name = "rust" path = "tests/rust.rs" required-features = ["alloc"] +[[test]] +name = "schemars_0_8" +path = "tests/schemars_0_8.rs" +required-features = ["schemars_0_8"] + [package.metadata.docs.rs] all-features = true rustdoc-args = [ diff --git a/serde_with/src/lib.rs b/serde_with/src/lib.rs index db73bb85..12d180a9 100644 --- a/serde_with/src/lib.rs +++ b/serde_with/src/lib.rs @@ -330,6 +330,8 @@ pub mod json; #[cfg(feature = "alloc")] mod key_value_map; pub mod rust; +#[cfg(feature = "schemars_0_8")] +mod schemars_0_8; pub mod ser; #[cfg(feature = "std")] mod serde_conv; @@ -2512,3 +2514,14 @@ pub struct SetPreventDuplicates(PhantomData); /// [`BTreeSet`]: std::collections::HashSet #[cfg(feature = "alloc")] pub struct SetLastValueWins(PhantomData); + +/// Helper for implementing [`JsonSchema`] on serializers whose output depends +/// on the type of the concrete field. +/// +/// It is added implicitly by the [`#[serde_as]`] macro when any `schemars` +/// feature is enabled. +/// +/// [`JsonSchema`]: ::schemars_0_8::JsonSchema +/// [`#[serde_as]`]: crate::serde_as +#[cfg(feature = "schemars_0_8")] +pub struct Schema(PhantomData, PhantomData); diff --git a/serde_with/src/schemars_0_8.rs b/serde_with/src/schemars_0_8.rs new file mode 100644 index 00000000..14811693 --- /dev/null +++ b/serde_with/src/schemars_0_8.rs @@ -0,0 +1,224 @@ +//! Integration with [schemars v0.8](schemars_0_8). +//! +//! This module is only available if using the `schemars_0_8` feature of the crate. + +use crate::prelude::{Schema as WrapSchema, *}; +use ::schemars_0_8::{ + gen::SchemaGenerator, + schema::{ArrayValidation, InstanceType, Schema, SchemaObject}, + JsonSchema, +}; +use std::borrow::Cow; + +//=================================================================== +// Macro helpers + +macro_rules! forward_schema { + ($fwd:ty) => { + fn schema_name() -> String { + <$fwd as JsonSchema>::schema_name() + } + + fn schema_id() -> Cow<'static, str> { + <$fwd as JsonSchema>::schema_id() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + <$fwd as JsonSchema>::json_schema(gen) + } + + fn is_referenceable() -> bool { + <$fwd as JsonSchema>::is_referenceable() + } + }; +} + +//=================================================================== +// Common definitions for various std types + +impl<'a, T: 'a, TA: 'a> JsonSchema for WrapSchema<&'a T, &'a TA> +where + T: ?Sized, + WrapSchema: JsonSchema, +{ + forward_schema!(&'a WrapSchema); +} + +impl<'a, T: 'a, TA: 'a> JsonSchema for WrapSchema<&'a mut T, &'a mut TA> +where + T: ?Sized, + WrapSchema: JsonSchema, +{ + forward_schema!(&'a mut WrapSchema); +} + +impl JsonSchema for WrapSchema, Option> +where + WrapSchema: JsonSchema, +{ + forward_schema!(Option>); +} + +impl JsonSchema for WrapSchema, Box> +where + T: ?Sized, + WrapSchema: JsonSchema, +{ + forward_schema!(Box>); +} + +impl JsonSchema for WrapSchema, Rc> +where + T: ?Sized, + WrapSchema: JsonSchema, +{ + forward_schema!(Rc>); +} + +impl JsonSchema for WrapSchema, Arc> +where + T: ?Sized, + WrapSchema: JsonSchema, +{ + forward_schema!(Arc>); +} + +impl JsonSchema for WrapSchema, Vec> +where + WrapSchema: JsonSchema, +{ + forward_schema!(Vec>); +} + +impl JsonSchema for WrapSchema, VecDeque> +where + WrapSchema: JsonSchema, +{ + forward_schema!(VecDeque>); +} + +// schemars only requires that V implement JsonSchema for BTreeMap +impl JsonSchema for WrapSchema, BTreeMap> +where + WrapSchema: JsonSchema, +{ + forward_schema!(BTreeMap, WrapSchema>); +} + +// schemars only requires that V implement JsonSchema for HashMap +impl JsonSchema for WrapSchema, HashMap> +where + WrapSchema: JsonSchema, +{ + forward_schema!(HashMap, WrapSchema, S>); +} + +impl JsonSchema for WrapSchema, BTreeSet> +where + WrapSchema: JsonSchema, +{ + forward_schema!(BTreeSet>); +} + +impl JsonSchema for WrapSchema, HashSet> +where + WrapSchema: JsonSchema, +{ + forward_schema!(HashSet, H>); +} + +impl JsonSchema for WrapSchema<[T; N], [TA; N]> +where + WrapSchema: JsonSchema, +{ + fn schema_name() -> String { + std::format!("[{}; {}]", >::schema_name(), N) + } + + fn schema_id() -> Cow<'static, str> { + std::format!("[{}; {}]", >::schema_id(), N).into() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + let (max, min) = match N.try_into() { + Ok(len) => (Some(len), Some(len)), + Err(_) => (None, Some(u32::MAX)), + }; + + SchemaObject { + instance_type: Some(InstanceType::Array.into()), + array: Some(Box::new(ArrayValidation { + items: Some(gen.subschema_for::>().into()), + max_items: max, + min_items: min, + ..Default::default() + })), + ..Default::default() + } + .into() + } + + fn is_referenceable() -> bool { + false + } +} + +macro_rules! schema_for_tuple { + ( + ( $( $ts:ident )+ ) + ( $( $as:ident )+ ) + ) => { + impl<$($ts,)+ $($as,)+> JsonSchema for WrapSchema<($($ts,)+), ($($as,)+)> + where + $( WrapSchema<$ts, $as>: JsonSchema, )+ + { + forward_schema!(( $( WrapSchema<$ts, $as>, )+ )); + } + } +} + +impl JsonSchema for WrapSchema<(), ()> { + forward_schema!(()); +} + +// schemars only implements JsonSchema for tuples up to 15 elements so we do +// the same here. +schema_for_tuple!((T0)(A0)); +schema_for_tuple!((T0 T1) (A0 A1)); +schema_for_tuple!((T0 T1 T2) (A0 A1 A2)); +schema_for_tuple!((T0 T1 T2 T3) (A0 A1 A2 A3)); +schema_for_tuple!((T0 T1 T2 T3 T4) (A0 A1 A2 A3 A4)); +schema_for_tuple!((T0 T1 T2 T3 T4 T5) (A0 A1 A2 A3 A4 A5)); +schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6) (A0 A1 A2 A3 A4 A5 A6)); +schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7) (A0 A1 A2 A3 A4 A5 A6 A7)); +schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8) (A0 A1 A2 A3 A4 A5 A6 A7 A8)); +schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8 T9) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9)); +schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10)); +schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11)); +schema_for_tuple!( + (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12) + (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12) +); +schema_for_tuple!( + (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13) + (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13) +); +schema_for_tuple!( + (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14) + (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14) +); +schema_for_tuple!( + (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15) + (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15) +); + +//=================================================================== +// Impls for serde_with types. + +impl JsonSchema for WrapSchema { + forward_schema!(T); +} + +impl JsonSchema for WrapSchema { + forward_schema!(String); +} diff --git a/serde_with/tests/schemars_0_8.rs b/serde_with/tests/schemars_0_8.rs new file mode 100644 index 00000000..b3d89127 --- /dev/null +++ b/serde_with/tests/schemars_0_8.rs @@ -0,0 +1,234 @@ +use crate::utils::{check_matches_schema, check_valid_json_schema}; +use ::schemars_0_8::JsonSchema; +use expect_test::expect_file; +use serde::Serialize; +use serde_json::json; +use serde_with::*; + +// This avoids us having to add `#[schemars(crate = "::schemars_0_8")]` all +// over the place. We're not testing that and it is inconvenient. +extern crate schemars_0_8 as schemars; + +mod utils; + +/// Declare a snapshot tests for a struct. +/// +/// The snapshot files are stored under the `schemars_0_8` folder alongside +/// this test file. +macro_rules! declare_snapshot_test { + {$( + $( #[$tattr:meta] )* + $test:ident { + $( #[$stattr:meta] )* + struct $name:ident { + $( + $( #[ $fattr:meta ] )* + $field:ident : $ty:ty + ),* + $(,)? + } + } + )*} => {$( + #[test] + $(#[$tattr])* + fn $test() { + #[serde_with::serde_as] + #[derive(JsonSchema, Serialize)] + $( #[$stattr] )* + struct $name { + $( + $( #[$fattr] )* + $field: $ty, + )* + } + + let schema = schemars::schema_for!($name); + let schema = serde_json::to_string_pretty(&schema) + .expect("schema could not be serialized"); + + let filename = concat!("./", module_path!(), "::", stringify!($test), ".json") + .replace("::", "/"); + + let expected = expect_file![filename]; + expected.assert_eq(&schema); + } + )*} +} + +#[test] +fn schemars_basic() { + use ::schemars_0_8::JsonSchema; + use serde::Serialize; + + #[serde_with::serde_as] + #[derive(JsonSchema, Serialize)] + #[schemars(crate = "::schemars_0_8")] + struct Basic { + /// Basic field, no attribute + bare_field: u32, + + /// Field that directly uses DisplayFromStr + #[serde_as(as = "DisplayFromStr")] + display_from_str: u32, + + /// Same does not implement JsonSchema directly so this checks that the + /// correct schemars attribute was injected. + #[serde_as(as = "Same")] + same: u32, + + /// This checks that Same still works when wrapped in a box. + #[serde_as(as = "Box")] + box_same: Box, + + /// Same thing, but with a Vec this time. + #[serde_as(as = "Vec<_>")] + vec_same: Vec, + } + + let schema = schemars::schema_for!(Basic); + let schema = serde_json::to_string_pretty(&schema).expect("schema could not be serialized"); + + let expected = expect_file!["./schemars_0_8/schemars_basic.json"]; + expected.assert_eq(&schema); +} + +mod test_std { + use super::*; + use std::collections::{BTreeMap, BTreeSet, VecDeque}; + + declare_snapshot_test! { + option { + struct Test { + #[serde_with(as = "Option<_>")] + optional: Option, + } + } + + vec { + struct Test { + #[serde_with(as = "Vec<_>")] + vec: Vec + } + } + + vec_deque { + struct Test { + #[serde_with(as = "VecDeque<_>")] + vec_deque: VecDeque + } + } + + map { + struct Test { + #[serde_with(as = "BTreeMap<_, _>")] + map: BTreeMap + } + } + + set { + struct Test { + #[serde_with(as = "BTreeSet<_>")] + map: BTreeSet, + } + } + + tuples { + struct Test { + #[serde_with(as = "()")] + tuple0: (), + + #[serde_with(as = "(_ ,)")] + tuple1: (i32,), + + #[serde_with(as = "(_, _)")] + tuple2: (i32, i32), + + #[serde_with(as = "(_, _, _)")] + tuple3: (i32, i32, String) + } + } + } +} + +mod derive { + use super::*; + + #[serde_with::serde_as] + #[derive(Serialize)] + #[cfg_attr(all(), derive(JsonSchema))] + struct Enabled { + #[serde_as(as = "DisplayFromStr")] + field: u32, + } + + #[serde_with::serde_as] + #[derive(Serialize)] + #[cfg_attr(any(), derive(JsonSchema))] + struct Disabled { + // If we are incorrectly adding `#[schemars(with = ...)]` attributes + // then we should get an error on this field. + #[serde_as(as = "DisplayFromStr")] + field: u32, + } + + #[test] + fn test_enabled_has_correct_schema() { + check_valid_json_schema(&Enabled { field: 77 }); + } +} + +mod array { + use super::*; + + #[serde_with::serde_as] + #[derive(JsonSchema, Serialize)] + struct FixedArray { + #[serde_as(as = "[_; 3]")] + array: [u32; 3], + } + + #[test] + fn test_serialized_is_valid() { + let array = FixedArray { array: [1, 2, 3] }; + + check_valid_json_schema(&array); + } + + #[test] + fn test_valid_json() { + let value = json!({ "array": [1, 2, 3] }); + check_matches_schema::(&value); + } + + #[test] + #[should_panic] + fn test_too_short() { + check_matches_schema::(&json!({ + "array": [1], + })); + } + + #[test] + #[should_panic] + fn test_too_long() { + check_matches_schema::(&json!({ + "array": [1, 2, 3, 4] + })); + } + + #[test] + #[should_panic] + fn test_wrong_item_type() { + check_matches_schema::(&json!({ + "array": ["1", "2", "3"] + })); + } + + #[test] + #[should_panic] + fn test_oob_item() { + check_matches_schema::(&json!({ + "array": [-1, 0x1_0000_0000i64, 32] + })) + } +} diff --git a/serde_with/tests/schemars_0_8/schemars_basic.json b/serde_with/tests/schemars_0_8/schemars_basic.json new file mode 100644 index 00000000..bf5e2e26 --- /dev/null +++ b/serde_with/tests/schemars_0_8/schemars_basic.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Basic", + "type": "object", + "required": [ + "bare_field", + "box_same", + "display_from_str", + "same", + "vec_same" + ], + "properties": { + "bare_field": { + "description": "Basic field, no attribute", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "box_same": { + "description": "This checks that Same still works when wrapped in a box.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "display_from_str": { + "description": "Field that directly uses DisplayFromStr", + "type": "string" + }, + "same": { + "description": "Same does not implement JsonSchema directly so this checks that the correct schemars attribute was injected.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "vec_same": { + "description": "Same thing, but with a Vec this time.", + "type": "array", + "items": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } +} \ No newline at end of file diff --git a/serde_with/tests/schemars_0_8/test_std/map.json b/serde_with/tests/schemars_0_8/test_std/map.json new file mode 100644 index 00000000..bbf223ac --- /dev/null +++ b/serde_with/tests/schemars_0_8/test_std/map.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "type": "object", + "required": [ + "map" + ], + "properties": { + "map": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } +} \ No newline at end of file diff --git a/serde_with/tests/schemars_0_8/test_std/option.json b/serde_with/tests/schemars_0_8/test_std/option.json new file mode 100644 index 00000000..9881456c --- /dev/null +++ b/serde_with/tests/schemars_0_8/test_std/option.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "type": "object", + "properties": { + "optional": { + "type": [ + "integer", + "null" + ], + "format": "int32" + } + } +} \ No newline at end of file diff --git a/serde_with/tests/schemars_0_8/test_std/set.json b/serde_with/tests/schemars_0_8/test_std/set.json new file mode 100644 index 00000000..f7ae4fc0 --- /dev/null +++ b/serde_with/tests/schemars_0_8/test_std/set.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "type": "object", + "required": [ + "map" + ], + "properties": { + "map": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } +} \ No newline at end of file diff --git a/serde_with/tests/schemars_0_8/test_std/tuples.json b/serde_with/tests/schemars_0_8/test_std/tuples.json new file mode 100644 index 00000000..9a9b4b44 --- /dev/null +++ b/serde_with/tests/schemars_0_8/test_std/tuples.json @@ -0,0 +1,60 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "type": "object", + "required": [ + "tuple0", + "tuple1", + "tuple2", + "tuple3" + ], + "properties": { + "tuple0": { + "type": "null" + }, + "tuple1": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + } + ], + "maxItems": 1, + "minItems": 1 + }, + "tuple2": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "integer", + "format": "int32" + } + ], + "maxItems": 2, + "minItems": 2 + }, + "tuple3": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "integer", + "format": "int32" + }, + { + "type": "string" + } + ], + "maxItems": 3, + "minItems": 3 + } + } +} \ No newline at end of file diff --git a/serde_with/tests/schemars_0_8/test_std/vec.json b/serde_with/tests/schemars_0_8/test_std/vec.json new file mode 100644 index 00000000..839850fe --- /dev/null +++ b/serde_with/tests/schemars_0_8/test_std/vec.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "type": "object", + "required": [ + "vec" + ], + "properties": { + "vec": { + "type": "array", + "items": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/serde_with/tests/schemars_0_8/test_std/vec_deque.json b/serde_with/tests/schemars_0_8/test_std/vec_deque.json new file mode 100644 index 00000000..aeca2452 --- /dev/null +++ b/serde_with/tests/schemars_0_8/test_std/vec_deque.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "type": "object", + "required": [ + "vec_deque" + ], + "properties": { + "vec_deque": { + "type": "array", + "items": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/serde_with/tests/utils.rs b/serde_with/tests/utils.rs index d9247fff..e86d3868 100644 --- a/serde_with/tests/utils.rs +++ b/serde_with/tests/utils.rs @@ -77,3 +77,62 @@ where .to_string(), ) } + +#[track_caller] +pub fn check_matches_schema(value: &serde_json::Value) +where + T: schemars_0_8::JsonSchema, +{ + use jsonschema::JSONSchema; + use std::fmt::Write; + + if cfg!(feature = "schemars_0_8") { + let schema_object = serde_json::to_value(schemars_0_8::schema_for!(T)) + .expect("schema for T could not be serialized to json"); + let schema = match JSONSchema::compile(&schema_object) { + Ok(schema) => schema, + Err(e) => panic!("schema for T was not a valid JSON schema: {e}"), + }; + + if let Err(errs) = schema.validate(value) { + let mut message = String::new(); + + let _ = writeln!( + &mut message, + "Object was not valid according to its own schema:" + ); + + for err in errs { + let _ = writeln!(&mut message, " -> {}", err); + } + + let _ = writeln!(&mut message); + let _ = writeln!(&mut message, "Object Value:"); + let _ = writeln!( + &mut message, + "{}", + serde_json::to_string_pretty(&value).unwrap_or_else(|e| format!("> error: {e}")) + ); + let _ = writeln!(&mut message); + let _ = writeln!(&mut message, "JSON Schema:"); + let _ = writeln!( + &mut message, + "{}", + serde_json::to_string_pretty(&schema_object) + .unwrap_or_else(|e| format!("> error: {e}")) + ); + + panic!("{}", message); + }; + } +} + +#[track_caller] +pub fn check_valid_json_schema(value: &T) +where + T: schemars_0_8::JsonSchema + Serialize, +{ + let value = serde_json::to_value(value).expect("could not serialize T to json"); + + check_matches_schema::(&value); +} diff --git a/serde_with_macros/Cargo.toml b/serde_with_macros/Cargo.toml index e8e4ccc8..cba83dbf 100644 --- a/serde_with_macros/Cargo.toml +++ b/serde_with_macros/Cargo.toml @@ -32,6 +32,9 @@ proc-macro = true [badges] maintenance = {status = "actively-developed"} +[features] +schemars_0_8 = [] + [dependencies] darling = "0.20.0" proc-macro2 = "1.0.1" diff --git a/serde_with_macros/src/lib.rs b/serde_with_macros/src/lib.rs index 83f0d6f4..061cb02e 100644 --- a/serde_with_macros/src/lib.rs +++ b/serde_with_macros/src/lib.rs @@ -47,7 +47,7 @@ extern crate proc_macro; mod apply; mod utils; -use crate::utils::{split_with_de_lifetime, DeriveOptions, IteratorExt as _}; +use crate::utils::{split_with_de_lifetime, DeriveOptions, IteratorExt as _, SchemaFieldConfig}; use darling::{ ast::NestedMeta, util::{Flag, Override}, @@ -586,6 +586,22 @@ fn field_has_attribute(field: &Field, namespace: &str, name: &str) -> bool { /// } /// ``` /// +/// # A note on `schemars` integration +/// When the `schemars_0_8` feature is enabled this macro will scan for +/// `#[derive(JsonSchema)]` attributes and, if found, will add +/// `#[schemars(with = "Schema")]` annotations to any fields with a +/// `#[serde_as(as = ...)]` annotation. If you wish to override the default +/// behavior here you can add `#[serde_as(schemars = true)]` or +/// `#[serde_as(schemars = false)]`. +/// +/// Note that this macro will check for any of the following derive paths: +/// * `JsonSchema` +/// * `schemars::JsonSchema` +/// * `::schemars::JsonSchema` +/// +/// It will also work if the relevant derive is behind a `#[cfg_attr]` attribute +/// and propagate the `#[cfg_attr]` to the various `#[schemars]` field attributes. +/// /// [`serde_as`]: https://docs.rs/serde_with/3.4.0/serde_with/guide/index.html /// [re-exporting `serde_as`]: https://docs.rs/serde_with/3.4.0/serde_with/guide/serde_as/index.html#re-exporting-serde_as #[proc_macro_attribute] @@ -594,6 +610,8 @@ pub fn serde_as(args: TokenStream, input: TokenStream) -> TokenStream { struct SerdeContainerOptions { #[darling(rename = "crate")] alt_crate_path: Option, + #[darling(rename = "schemars")] + enable_schemars_support: Option, } match NestedMeta::parse_meta_list(args.into()) { @@ -609,11 +627,18 @@ pub fn serde_as(args: TokenStream, input: TokenStream) -> TokenStream { .alt_crate_path .unwrap_or_else(|| syn::parse_quote!(::serde_with)); + let schemars_config = match container_options.enable_schemars_support { + _ if cfg!(not(feature = "schemars_0_8")) => SchemaFieldConfig::Disabled, + Some(true) => SchemaFieldConfig::Unconditional, + Some(false) => SchemaFieldConfig::Disabled, + None => crate::utils::has_derive_jsonschema(input.clone()), + }; + // Convert any error message into a nice compiler error let res = apply_function_to_struct_and_enum_fields_darling( input, &serde_with_crate_path, - |field| serde_as_add_attr_to_field(field, &serde_with_crate_path), + |field| serde_as_add_attr_to_field(field, &serde_with_crate_path, &schemars_config), ) .unwrap_or_else(darling::Error::write_errors); TokenStream::from(res) @@ -626,10 +651,14 @@ pub fn serde_as(args: TokenStream, input: TokenStream) -> TokenStream { fn serde_as_add_attr_to_field( field: &mut Field, serde_with_crate_path: &Path, + schemars_config: &SchemaFieldConfig, ) -> Result<(), DarlingError> { #[derive(FromField)] #[darling(attributes(serde_as))] struct SerdeAsOptions { + /// The original type of the field + ty: Type, + r#as: Option, deserialize_as: Option, serialize_as: Option, @@ -741,6 +770,7 @@ fn serde_as_add_attr_to_field( return Err(DarlingError::multiple(errors)); } + let type_original = &serde_as_options.ty; let type_same = &syn::parse_quote!(#serde_with_crate_path::Same); if let Some(type_) = &serde_as_options.r#as { emit_borrow_annotation(&serde_options, type_, field); @@ -750,6 +780,14 @@ fn serde_as_add_attr_to_field( let attr_inner_tokens = quote!(#serde_with_crate_path::As::<#replacement_type>).to_string(); let attr = parse_quote!(#[serde(with = #attr_inner_tokens)]); field.attrs.push(attr); + + if let Some(cfg) = schemars_config.cfg_expr() { + let attr_inner_tokens = + quote!(#serde_with_crate_path::Schema::<#type_original, #replacement_type>) + .to_string(); + let attr = parse_quote!(#[cfg_attr(#cfg, schemars(with = #attr_inner_tokens))]); + field.attrs.push(attr); + } } if let Some(type_) = &serde_as_options.deserialize_as { emit_borrow_annotation(&serde_options, type_, field); @@ -760,13 +798,31 @@ fn serde_as_add_attr_to_field( quote!(#serde_with_crate_path::As::<#replacement_type>::deserialize).to_string(); let attr = parse_quote!(#[serde(deserialize_with = #attr_inner_tokens)]); field.attrs.push(attr); + + if let Some(cfg) = schemars_config.cfg_expr() { + let attr_inner_tokens = + quote!(#serde_with_crate_path::Schema::<#type_original, #replacement_type>::deserialize) + .to_string(); + let attr = + parse_quote!(#[cfg_attr(#cfg, schemars(deserialize_with = #attr_inner_tokens))]); + field.attrs.push(attr); + } } if let Some(type_) = serde_as_options.serialize_as { - let replacement_type = replace_infer_type_with_type(type_, type_same); + let replacement_type = replace_infer_type_with_type(type_.clone(), type_same); let attr_inner_tokens = quote!(#serde_with_crate_path::As::<#replacement_type>::serialize).to_string(); let attr = parse_quote!(#[serde(serialize_with = #attr_inner_tokens)]); field.attrs.push(attr); + + if let Some(cfg) = schemars_config.cfg_expr() { + let attr_inner_tokens = + quote!(#serde_with_crate_path::Schema::<#type_original, #replacement_type>::serialize) + .to_string(); + let attr = + parse_quote!(#[cfg_attr(#cfg, schemars(serialize_with = #attr_inner_tokens))]); + field.attrs.push(attr); + } } Ok(()) diff --git a/serde_with_macros/src/utils.rs b/serde_with_macros/src/utils.rs index 068cbce6..e8f90c93 100644 --- a/serde_with_macros/src/utils.rs +++ b/serde_with_macros/src/utils.rs @@ -3,7 +3,7 @@ use darling::FromDeriveInput; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::ToTokens; -use syn::{parse_quote, Error, Generics, Path, TypeGenerics}; +use syn::{parse_quote, punctuated::Punctuated, Error, Generics, Path, TypeGenerics}; /// Merge multiple [`syn::Error`] into one. pub(crate) trait IteratorExt { @@ -75,3 +75,108 @@ impl<'a> ToTokens for DeImplGenerics<'a> { impl_generics.to_tokens(tokens); } } + +/// Determine if there is a `#[derive(JsonSchema)]` on this struct. +pub(crate) fn has_derive_jsonschema(input: TokenStream) -> SchemaFieldConfig { + /// Represents the macro body of a `#[cfg_attr]` attribute. + /// + /// ```text + /// #[cfg_attr(feature = "things", derive(Macro))] + /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// ``` + struct CfgAttr { + cfg: syn::Expr, + _comma: syn::Token![,], + meta: syn::Meta, + } + + impl syn::parse::Parse for CfgAttr { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + Ok(Self { + cfg: input.parse()?, + _comma: input.parse()?, + meta: input.parse()?, + }) + } + } + + fn parse_derive_args( + input: syn::parse::ParseStream<'_>, + ) -> syn::Result> { + Punctuated::parse_terminated_with(input, Path::parse_mod_style) + } + + fn eval_attribute(attr: &syn::Attribute) -> syn::Result { + let args: CfgAttr; + let mut meta = &attr.meta; + let mut config = SchemaFieldConfig::Unconditional; + + if meta.path().is_ident("cfg_attr") { + let list = meta.require_list()?; + args = list.parse_args()?; + + meta = &args.meta; + config = SchemaFieldConfig::Conditional(args.cfg); + } + + let list = meta.require_list()?; + if !list.path.is_ident("derive") { + return Ok(SchemaFieldConfig::Disabled); + } + + let derives = list.parse_args_with(parse_derive_args)?; + for derive in &derives { + let segments = &derive.segments; + + // Check for $(::)? $(schemars::)? JsonSchema + match segments.len() { + 1 if segments[0].ident == "JsonSchema" => (), + 2 if segments[0].ident == "schemars" && segments[1].ident == "JsonSchema" => (), + _ => continue, + } + + return Ok(config); + } + + Ok(SchemaFieldConfig::Disabled) + } + + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(_) => return SchemaFieldConfig::Disabled, + }; + + for attr in input.attrs { + match eval_attribute(&attr) { + Ok(SchemaFieldConfig::Disabled) => continue, + Ok(config) => return config, + Err(_) => continue, + } + } + + SchemaFieldConfig::Disabled +} + +/// Enum controlling when we should emit a `#[schemars]` field attribute. +pub(crate) enum SchemaFieldConfig { + /// Emit a `#[cfg_attr(#cfg, schemars(...))]` attribute. + Conditional(syn::Expr), + /// Emit a `#[schemars(...)]` attribute (or equivalent). + Unconditional, + /// Do not emit an attribute. + Disabled, +} + +impl SchemaFieldConfig { + /// Get a `#[cfg]` expression suitable for emitting the `#[schemars]` attribute. + /// + /// If this config is `Unconditional` then it will just return `all()` which + /// is always true. + pub(crate) fn cfg_expr(&self) -> Option { + match self { + Self::Unconditional => Some(syn::parse_quote!(all())), + Self::Conditional(cfg) => Some(cfg.clone()), + Self::Disabled => None, + } + } +}