diff --git a/Cargo.lock b/Cargo.lock index 6d30db2d5..c938a64af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -539,24 +545,19 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", + "allocator-api2", ] -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -683,7 +684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", "serde", ] @@ -752,9 +753,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "match_cfg" @@ -823,10 +824,11 @@ dependencies = [ [[package]] name = "newtype-uuid" -version = "1.1.0" +version = "1.2.0" dependencies = [ "schemars", "serde", + "serde_json", "uuid", ] @@ -955,18 +957,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -993,11 +995,11 @@ dependencies = [ [[package]] name = "regress" -version = "0.7.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ed9969cad8051328011596bf549629f1b800cf1731e7964b1eef8dfc480d2c2" +checksum = "0eae2a1ebfecc58aff952ef8ccd364329abe627762f5bf09ff42eb9d98522479" dependencies = [ - "hashbrown 0.13.2", + "hashbrown", "memchr", ] @@ -1105,20 +1107,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + [[package]] name = "serde" -version = "1.0.196" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", @@ -1138,9 +1149,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -1310,9 +1321,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.48" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -1338,18 +1349,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", @@ -1527,9 +1538,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typify" -version = "0.0.15" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ed4d717aa95e598e2f9183376b060e95669ef8f444701ea6afb990fde1cf69" +checksum = "adb6beec125971dda80a086f90b4a70f60f222990ce4d63ad0fc140492f53444" dependencies = [ "typify-impl", "typify-macro", @@ -1537,9 +1548,9 @@ dependencies = [ [[package]] name = "typify-impl" -version = "0.0.15" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89057244dfade7c58af9e62beccbcbeb7a7e7701697a33b06dbe0b7331fb79cf" +checksum = "93bbb24e990654aff858d80fee8114f4322f7d7a1b1ecb45129e2fcb0d0ad5ae" dependencies = [ "heck", "log", @@ -1547,6 +1558,8 @@ dependencies = [ "quote", "regress", "schemars", + "semver", + "serde", "serde_json", "syn", "thiserror", @@ -1555,13 +1568,14 @@ dependencies = [ [[package]] name = "typify-macro" -version = "0.0.15" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ddade397f5957d2cd7fb27f905a9a569db20e8e1e3ea589edce40be07b92825" +checksum = "f8e6491896e955692d68361c68db2b263e3bec317ec0b684e0e2fa882fb6e31e" dependencies = [ "proc-macro2", "quote", "schemars", + "semver", "serde", "serde_json", "serde_tokenstream", diff --git a/crates/integration-tests/Cargo.toml b/crates/integration-tests/Cargo.toml index 66f6a00c7..ce2a5b164 100644 --- a/crates/integration-tests/Cargo.toml +++ b/crates/integration-tests/Cargo.toml @@ -14,7 +14,7 @@ schemars = { version = "0.8.17", optional = true } serde = { version = "1", optional = true } serde_json = { version = "1.0.115", optional = true } syn = { version = "2.0.48", optional = true } -typify = { version = "0.0.15", optional = true } +typify = { version = "0.1.0", optional = true } [features] internal-schemars08-tests = [ diff --git a/crates/integration-tests/outputs/schema-rust-with-extension.rs b/crates/integration-tests/outputs/schema-rust-with-extension.rs new file mode 100644 index 000000000..0c2bf06e9 --- /dev/null +++ b/crates/integration-tests/outputs/schema-rust-with-extension.rs @@ -0,0 +1,54 @@ +/// Error types. +pub mod error { + /// Error from a TryFrom or FromStr implementation. + pub struct ConversionError(std::borrow::Cow<'static, str>); + impl std::error::Error for ConversionError {} + impl std::fmt::Display for ConversionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + std::fmt::Display::fmt(&self.0, f) + } + } + impl std::fmt::Debug for ConversionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + std::fmt::Debug::fmt(&self.0, f) + } + } + impl From<&'static str> for ConversionError { + fn from(value: &'static str) -> Self { + Self(value.into()) + } + } + impl From for ConversionError { + fn from(value: String) -> Self { + Self(value.into()) + } + } +} +///MyPathStruct +/// +///
JSON schema +/// +/// ```json +///{ +/// "title": "MyPathStruct", +/// "type": "object", +/// "required": [ +/// "id" +/// ], +/// "properties": { +/// "id": { +/// "$ref": "#/definitions/TypedUuidForMyKind" +/// } +/// } +///} +/// ``` +///
+#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct MyPathStruct { + pub id: ::newtype_uuid::TypedUuid<::my_crate::MyKind>, +} +impl From<&MyPathStruct> for MyPathStruct { + fn from(value: &MyPathStruct) -> Self { + value.clone() + } +} diff --git a/crates/integration-tests/outputs/schema-rust-with-replace.rs b/crates/integration-tests/outputs/schema-rust-with-replace.rs index 014351739..0c2bf06e9 100644 --- a/crates/integration-tests/outputs/schema-rust-with-replace.rs +++ b/crates/integration-tests/outputs/schema-rust-with-replace.rs @@ -1,20 +1,46 @@ +/// Error types. +pub mod error { + /// Error from a TryFrom or FromStr implementation. + pub struct ConversionError(std::borrow::Cow<'static, str>); + impl std::error::Error for ConversionError {} + impl std::fmt::Display for ConversionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + std::fmt::Display::fmt(&self.0, f) + } + } + impl std::fmt::Debug for ConversionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + std::fmt::Debug::fmt(&self.0, f) + } + } + impl From<&'static str> for ConversionError { + fn from(value: &'static str) -> Self { + Self(value.into()) + } + } + impl From for ConversionError { + fn from(value: String) -> Self { + Self(value.into()) + } + } +} ///MyPathStruct /// ///
JSON schema /// /// ```json -/**{ - "title": "MyPathStruct", - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "$ref": "#/definitions/TypedUuidForMyKind" - } - } -}*/ +///{ +/// "title": "MyPathStruct", +/// "type": "object", +/// "required": [ +/// "id" +/// ], +/// "properties": { +/// "id": { +/// "$ref": "#/definitions/TypedUuidForMyKind" +/// } +/// } +///} /// ``` ///
#[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/crates/integration-tests/outputs/schema-rust.rs b/crates/integration-tests/outputs/schema-rust.rs index 8bd4704a5..c4c2db521 100644 --- a/crates/integration-tests/outputs/schema-rust.rs +++ b/crates/integration-tests/outputs/schema-rust.rs @@ -1,20 +1,46 @@ +/// Error types. +pub mod error { + /// Error from a TryFrom or FromStr implementation. + pub struct ConversionError(std::borrow::Cow<'static, str>); + impl std::error::Error for ConversionError {} + impl std::fmt::Display for ConversionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + std::fmt::Display::fmt(&self.0, f) + } + } + impl std::fmt::Debug for ConversionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + std::fmt::Debug::fmt(&self.0, f) + } + } + impl From<&'static str> for ConversionError { + fn from(value: &'static str) -> Self { + Self(value.into()) + } + } + impl From for ConversionError { + fn from(value: String) -> Self { + Self(value.into()) + } + } +} ///MyPathStruct /// ///
JSON schema /// /// ```json -/**{ - "title": "MyPathStruct", - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "$ref": "#/definitions/TypedUuidForMyKind" - } - } -}*/ +///{ +/// "title": "MyPathStruct", +/// "type": "object", +/// "required": [ +/// "id" +/// ], +/// "properties": { +/// "id": { +/// "$ref": "#/definitions/TypedUuidForMyKind" +/// } +/// } +///} /// ``` ///
#[derive(Clone, Debug, Deserialize, Serialize)] @@ -31,10 +57,25 @@ impl From<&MyPathStruct> for MyPathStruct { ///
JSON schema /// /// ```json -/**{ - "type": "string", - "format": "uuid" -}*/ +///{ +/// "type": "string", +/// "format": "uuid", +/// "x-rust-type": { +/// "crate": "newtype-uuid", +/// "parameters": [ +/// { +/// "not": true, +/// "x-rust-type": { +/// "crate": "my-crate", +/// "path": "my_crate::MyKind", +/// "version": "0.1.0" +/// } +/// } +/// ], +/// "path": "newtype_uuid::TypedUuid", +/// "version": "1.2.0" +/// } +///} /// ``` ///
#[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/crates/integration-tests/outputs/typed-uuid-schema.json b/crates/integration-tests/outputs/typed-uuid-schema.json index 5f196ed57..6fb9d48a4 100644 --- a/crates/integration-tests/outputs/typed-uuid-schema.json +++ b/crates/integration-tests/outputs/typed-uuid-schema.json @@ -13,7 +13,22 @@ "definitions": { "TypedUuidForMyKind": { "type": "string", - "format": "uuid" + "format": "uuid", + "x-rust-type": { + "crate": "newtype-uuid", + "parameters": [ + { + "not": true, + "x-rust-type": { + "crate": "my-crate", + "path": "my_crate::MyKind", + "version": "0.1.0" + } + } + ], + "path": "newtype_uuid::TypedUuid", + "version": "1.2.0" + } } } } \ No newline at end of file diff --git a/crates/integration-tests/src/json_schema.rs b/crates/integration-tests/src/json_schema.rs index 3b7ac3b7e..18446100c 100644 --- a/crates/integration-tests/src/json_schema.rs +++ b/crates/integration-tests/src/json_schema.rs @@ -8,9 +8,43 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use typify::TypeSpaceSettings; -#[derive(Debug, JsonSchema)] +#[derive(Debug)] enum MyKind {} +impl JsonSchema for MyKind { + fn schema_name() -> String { + "MyKind".to_string() + } + + fn is_referenceable() -> bool { + false + } + + fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + schemars::schema::SchemaObject { + subschemas: Some( + schemars::schema::SubschemaValidation { + not: Some(schemars::schema::Schema::Bool(true).into()), + ..Default::default() + } + .into(), + ), + extensions: [( + "x-rust-type".to_string(), + serde_json::json!({ + "crate": "my-crate", + "version": "0.1.0", + "path": "my_crate::MyKind", + }), + )] + .into_iter() + .collect(), + ..Default::default() + } + .into() + } +} + impl TypedUuidKind for MyKind { fn tag() -> TypedUuidTag { const TAG: TypedUuidTag = TypedUuidTag::new("my_kind"); @@ -59,8 +93,24 @@ fn test_json_schema_snapshot() { "::newtype_uuid::TypedUuid<::my_crate::MyKind>", std::iter::empty(), ); - let output = generate_schema_with(&settings, schema); + let output = generate_schema_with(&settings, schema.clone()); expectorate::assert_contents("outputs/schema-rust-with-replace.rs", &output); + + // And finally, using the x-rust-type extension we include. + let mut settings = TypeSpaceSettings::default(); + settings + .with_crate( + "newtype-uuid", + typify::CrateVers::Version("1.2.0".parse().unwrap()), + None, + ) + .with_crate( + "my-crate", + typify::CrateVers::Version("0.1.0".parse().unwrap()), + None, + ); + let output = generate_schema_with(&settings, schema); + expectorate::assert_contents("outputs/schema-rust-with-extension.rs", &output); } fn generate_schema_with( diff --git a/crates/newtype-uuid/Cargo.toml b/crates/newtype-uuid/Cargo.toml index e0be727d2..abd5ab162 100644 --- a/crates/newtype-uuid/Cargo.toml +++ b/crates/newtype-uuid/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "newtype-uuid" -version = "1.1.0" +version = "1.2.0" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/oxidecomputer/newtype-uuid" @@ -19,6 +19,7 @@ rustdoc-args = ["--cfg=doc_cfg"] [dependencies] serde = { version = "1", optional = true, features = ["derive"] } +serde_json = { version = "1.0.117", optional = true } schemars = { version = "0.8", features = ["uuid1"], optional = true } uuid = { version = "1.7.0", default-features = false } @@ -28,4 +29,4 @@ std = ["uuid/std", "alloc"] alloc = [] v4 = ["uuid/v4"] serde = ["dep:serde", "uuid/serde"] -schemars08 = ["dep:schemars", "std"] +schemars08 = ["dep:schemars", "dep:serde_json", "std"] diff --git a/crates/newtype-uuid/src/lib.rs b/crates/newtype-uuid/src/lib.rs index 96a1ce61b..a8ab66897 100644 --- a/crates/newtype-uuid/src/lib.rs +++ b/crates/newtype-uuid/src/lib.rs @@ -252,7 +252,11 @@ impl FromStr for TypedUuid { #[cfg(feature = "schemars08")] mod schemars08_imp { use super::*; - use schemars::JsonSchema; + use schemars::{ + schema::{InstanceType, SchemaObject}, + JsonSchema, + }; + use serde_json::json; /// Implements `JsonSchema` for `TypedUuid`, if `T` implements `JsonSchema`. /// @@ -275,7 +279,25 @@ mod schemars08_imp { #[inline] fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - Uuid::json_schema(gen) + SchemaObject { + instance_type: Some(InstanceType::String.into()), + format: Some("uuid".to_string()), + extensions: [( + "x-rust-type".to_string(), + json!({ + "crate": "newtype-uuid", + "version": "1.2.0", + "path": "newtype_uuid::TypedUuid", + "parameters": [ + gen.subschema_for::() + ] + }), + )] + .into_iter() + .collect(), + ..Default::default() + } + .into() } } }