From 16a4a193ecf2c520b0904225c9e2355eca7df2ef Mon Sep 17 00:00:00 2001 From: Stefan Merettig Date: Mon, 17 Mar 2025 12:04:39 +0100 Subject: [PATCH] Add schemars support --- Cargo.toml | 2 + src/lib.rs | 16 +++++ tests/schemars.json | 149 ++++++++++++++++++++++++++++++++++++++++++++ tests/schemars.rs | 9 +++ 4 files changed, 176 insertions(+) create mode 100644 tests/schemars.json create mode 100644 tests/schemars.rs diff --git a/Cargo.toml b/Cargo.toml index 4c8f1d2..205e4c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ diff = [] [dependencies] jsonptr = "0.7.1" +schemars = { version = "0.8", optional = true } serde = { version = "1.0.159", features = ["derive"] } serde_json = "1.0.95" thiserror = "1.0.40" @@ -24,6 +25,7 @@ utoipa = { version = "4.0", optional = true } [dev-dependencies] expectorate = "1.0" rand = "0.8.5" +schemars = "0.8.22" serde_json = { version = "1.0.95", features = ["preserve_order"] } serde_yaml = "0.9.19" utoipa = { version = "4.0", features = ["debug"] } diff --git a/src/lib.rs b/src/lib.rs index 870e9c0..42c974c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,6 +129,7 @@ macro_rules! impl_display { /// Representation of JSON Patch (list of patch operations) #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct Patch(pub Vec); @@ -144,10 +145,12 @@ impl std::ops::Deref for Patch { /// JSON Patch 'add' operation representation #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct AddOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. + #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))] #[cfg_attr(feature = "utoipa", schema(value_type = String))] pub path: PointerBuf, /// Value to add to the target location. @@ -158,10 +161,12 @@ impl_display!(AddOperation); /// JSON Patch 'remove' operation representation #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct RemoveOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. + #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))] #[cfg_attr(feature = "utoipa", schema(value_type = String))] pub path: PointerBuf, } @@ -170,10 +175,12 @@ impl_display!(RemoveOperation); /// JSON Patch 'replace' operation representation #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct ReplaceOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. + #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))] #[cfg_attr(feature = "utoipa", schema(value_type = String))] pub path: PointerBuf, /// Value to replace with. @@ -184,14 +191,17 @@ impl_display!(ReplaceOperation); /// JSON Patch 'move' operation representation #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct MoveOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// to move value from. + #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))] #[cfg_attr(feature = "utoipa", schema(value_type = String))] pub from: PointerBuf, /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. + #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))] #[cfg_attr(feature = "utoipa", schema(value_type = String))] pub path: PointerBuf, } @@ -200,14 +210,17 @@ impl_display!(MoveOperation); /// JSON Patch 'copy' operation representation #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct CopyOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// to copy value from. + #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))] #[cfg_attr(feature = "utoipa", schema(value_type = String))] pub from: PointerBuf, /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. + #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))] #[cfg_attr(feature = "utoipa", schema(value_type = String))] pub path: PointerBuf, } @@ -216,10 +229,12 @@ impl_display!(CopyOperation); /// JSON Patch 'test' operation representation #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct TestOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. + #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))] #[cfg_attr(feature = "utoipa", schema(value_type = String))] pub path: PointerBuf, /// Value to test against. @@ -230,6 +245,7 @@ impl_display!(TestOperation); /// JSON Patch single patch operation #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] #[serde(tag = "op")] #[serde(rename_all = "lowercase")] diff --git a/tests/schemars.json b/tests/schemars.json new file mode 100644 index 0000000..f61cff3 --- /dev/null +++ b/tests/schemars.json @@ -0,0 +1,149 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PatchOperation", + "description": "JSON Patch single patch operation", + "oneOf": [ + { + "description": "'add' operation", + "type": "object", + "required": [ + "op", + "path", + "value" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "add" + ] + }, + "path": { + "description": "JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location within the target document where the operation is performed.", + "type": "string" + }, + "value": { + "description": "Value to add to the target location." + } + } + }, + { + "description": "'remove' operation", + "type": "object", + "required": [ + "op", + "path" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "remove" + ] + }, + "path": { + "description": "JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location within the target document where the operation is performed.", + "type": "string" + } + } + }, + { + "description": "'replace' operation", + "type": "object", + "required": [ + "op", + "path", + "value" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "replace" + ] + }, + "path": { + "description": "JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location within the target document where the operation is performed.", + "type": "string" + }, + "value": { + "description": "Value to replace with." + } + } + }, + { + "description": "'move' operation", + "type": "object", + "required": [ + "from", + "op", + "path" + ], + "properties": { + "from": { + "description": "JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location to move value from.", + "type": "string" + }, + "op": { + "type": "string", + "enum": [ + "move" + ] + }, + "path": { + "description": "JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location within the target document where the operation is performed.", + "type": "string" + } + } + }, + { + "description": "'copy' operation", + "type": "object", + "required": [ + "from", + "op", + "path" + ], + "properties": { + "from": { + "description": "JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location to copy value from.", + "type": "string" + }, + "op": { + "type": "string", + "enum": [ + "copy" + ] + }, + "path": { + "description": "JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location within the target document where the operation is performed.", + "type": "string" + } + } + }, + { + "description": "'test' operation", + "type": "object", + "required": [ + "op", + "path", + "value" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "test" + ] + }, + "path": { + "description": "JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location within the target document where the operation is performed.", + "type": "string" + }, + "value": { + "description": "Value to test against." + } + } + } + ] +} \ No newline at end of file diff --git a/tests/schemars.rs b/tests/schemars.rs new file mode 100644 index 0000000..eee5daa --- /dev/null +++ b/tests/schemars.rs @@ -0,0 +1,9 @@ +#[cfg(feature = "schemars")] +#[test] +fn schema() { + use json_patch::*; + + let schema = schemars::schema_for!(PatchOperation); + let json = serde_json::to_string_pretty(&schema).unwrap(); + expectorate::assert_contents("tests/schemars.json", &json); +}