From 5f4b55c7c9871065fe40c06bbc2c8f008bb5bbac Mon Sep 17 00:00:00 2001 From: "Adam H. Leventhal" Date: Sat, 13 May 2023 09:01:07 -0700 Subject: [PATCH 1/4] WIP: use json schema for json schema --- typify/tests/schemas/reflexive.json | 245 +++++++++++++++- typify/tests/schemas/reflexive.rs | 415 +++++++++++++++++++++++++++- 2 files changed, 644 insertions(+), 16 deletions(-) diff --git a/typify/tests/schemas/reflexive.json b/typify/tests/schemas/reflexive.json index 7eb4e6e4..efcdf6ba 100644 --- a/typify/tests/schemas/reflexive.json +++ b/typify/tests/schemas/reflexive.json @@ -1,17 +1,244 @@ { - "$comment": "validate references to the whole", - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "node", - "type": "object", - "properties": { - "value": { - "type": "integer" + "$comment": "tests reflexive schemas with the json schema draft 7 schema", + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://json-schema.org/draft-07/schema", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#" + } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { + "$ref": "#/definitions/nonNegativeInteger" + }, + { + "default": 0 + } + ] }, - "children": { + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { "type": "array", "items": { + "type": "string" + }, + "uniqueItems": true, + "default": [] + } + }, + "type": [ + "object", + "boolean" + ], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { + "$ref": "#/definitions/nonNegativeInteger" + }, + "minLength": { + "$ref": "#/definitions/nonNegativeIntegerDefault0" + }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { + "$ref": "#" + }, + "items": { + "anyOf": [ + { + "$ref": "#" + }, + { + "$ref": "#/definitions/schemaArray" + } + ], + "default": true + }, + "maxItems": { + "$ref": "#/definitions/nonNegativeInteger" + }, + "minItems": { + "$ref": "#/definitions/nonNegativeIntegerDefault0" + }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { + "$ref": "#" + }, + "maxProperties": { + "$ref": "#/definitions/nonNegativeInteger" + }, + "minProperties": { + "$ref": "#/definitions/nonNegativeIntegerDefault0" + }, + "required": { + "$ref": "#/definitions/stringArray" + }, + "additionalProperties": { + "$ref": "#" + }, + "definitions": { + "type": "object", + "additionalProperties": { + "$ref": "#" + }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" + }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#" + }, + "propertyNames": { + "$comment": "this is the only change required from the original", + "type": "string", + "format": "regex" + }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "$ref": "#" + }, + { + "$ref": "#/definitions/stringArray" + } + ] } + }, + "propertyNames": { + "$ref": "#" + }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { + "$ref": "#/definitions/simpleTypes" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/simpleTypes" + }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { + "type": "string" + }, + "contentMediaType": { + "type": "string" + }, + "contentEncoding": { + "type": "string" + }, + "if": { + "$ref": "#" + }, + "then": { + "$ref": "#" + }, + "else": { + "$ref": "#" + }, + "allOf": { + "$ref": "#/definitions/schemaArray" + }, + "anyOf": { + "$ref": "#/definitions/schemaArray" + }, + "oneOf": { + "$ref": "#/definitions/schemaArray" + }, + "not": { + "$ref": "#" } - } + }, + "default": true } \ No newline at end of file diff --git a/typify/tests/schemas/reflexive.rs b/typify/tests/schemas/reflexive.rs index 02267dee..35b560b2 100644 --- a/typify/tests/schemas/reflexive.rs +++ b/typify/tests/schemas/reflexive.rs @@ -1,15 +1,416 @@ #[allow(unused_imports)] use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Node { - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub children: Vec, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub value: Option, +#[serde(untagged)] +pub enum CoreSchemaMetaSchema { + Boolean(bool), + Object { + #[serde( + rename = "additionalItems", + default, + skip_serializing_if = "Option::is_none" + )] + additional_items: Option>, + #[serde( + rename = "additionalProperties", + default, + skip_serializing_if = "Option::is_none" + )] + additional_properties: Option>, + #[serde(rename = "allOf", default, skip_serializing_if = "Option::is_none")] + all_of: Option, + #[serde(rename = "anyOf", default, skip_serializing_if = "Option::is_none")] + any_of: Option, + #[serde(rename = "$comment", default, skip_serializing_if = "Option::is_none")] + comment: Option, + #[serde(rename = "const", default, skip_serializing_if = "Option::is_none")] + const_: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + contains: Option>, + #[serde( + rename = "contentEncoding", + default, + skip_serializing_if = "Option::is_none" + )] + content_encoding: Option, + #[serde( + rename = "contentMediaType", + default, + skip_serializing_if = "Option::is_none" + )] + content_media_type: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + default: Option, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + definitions: std::collections::HashMap, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + dependencies: + std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + description: Option, + #[serde(rename = "else", default, skip_serializing_if = "Option::is_none")] + else_: Option>, + #[serde(rename = "enum", default, skip_serializing_if = "Option::is_none")] + enum_: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + examples: Vec, + #[serde( + rename = "exclusiveMaximum", + default, + skip_serializing_if = "Option::is_none" + )] + exclusive_maximum: Option, + #[serde( + rename = "exclusiveMinimum", + default, + skip_serializing_if = "Option::is_none" + )] + exclusive_minimum: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + format: Option, + #[serde(rename = "$id", default, skip_serializing_if = "Option::is_none")] + id: Option, + #[serde(rename = "if", default, skip_serializing_if = "Option::is_none")] + if_: Option>, + #[serde(default = "defaults::core_schema_meta_schema_object_items")] + items: CoreSchemaMetaSchemaObjectItems, + #[serde(rename = "maxItems", default, skip_serializing_if = "Option::is_none")] + max_items: Option, + #[serde(rename = "maxLength", default, skip_serializing_if = "Option::is_none")] + max_length: Option, + #[serde( + rename = "maxProperties", + default, + skip_serializing_if = "Option::is_none" + )] + max_properties: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + maximum: Option, + #[serde(rename = "minItems", default, skip_serializing_if = "Option::is_none")] + min_items: Option, + #[serde(rename = "minLength", default, skip_serializing_if = "Option::is_none")] + min_length: Option, + #[serde( + rename = "minProperties", + default, + skip_serializing_if = "Option::is_none" + )] + min_properties: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + minimum: Option, + #[serde( + rename = "multipleOf", + default, + skip_serializing_if = "Option::is_none" + )] + multiple_of: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + not: Option>, + #[serde(rename = "oneOf", default, skip_serializing_if = "Option::is_none")] + one_of: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pattern: Option, + #[serde( + rename = "patternProperties", + default, + skip_serializing_if = "std::collections::HashMap::is_empty" + )] + pattern_properties: std::collections::HashMap, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + properties: std::collections::HashMap, + #[serde( + rename = "propertyNames", + default, + skip_serializing_if = "Option::is_none" + )] + property_names: Option>, + #[serde(rename = "readOnly", default)] + read_only: bool, + #[serde(rename = "$ref", default, skip_serializing_if = "Option::is_none")] + ref_: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + required: Option, + #[serde(rename = "$schema", default, skip_serializing_if = "Option::is_none")] + schema: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + then: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + title: Option, + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + type_: Option, + #[serde(rename = "uniqueItems", default)] + unique_items: bool, + }, } -impl From<&Node> for Node { - fn from(value: &Node) -> Self { +impl From<&CoreSchemaMetaSchema> for CoreSchemaMetaSchema { + fn from(value: &CoreSchemaMetaSchema) -> Self { value.clone() } } +impl Default for CoreSchemaMetaSchema { + fn default() -> Self { + CoreSchemaMetaSchema::Boolean(true) + } +} +impl From for CoreSchemaMetaSchema { + fn from(value: bool) -> Self { + Self::Boolean(value) + } +} +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum CoreSchemaMetaSchemaObjectDependenciesValue { + Variant0(CoreSchemaMetaSchema), + Variant1(StringArray), +} +impl From<&CoreSchemaMetaSchemaObjectDependenciesValue> + for CoreSchemaMetaSchemaObjectDependenciesValue +{ + fn from(value: &CoreSchemaMetaSchemaObjectDependenciesValue) -> Self { + value.clone() + } +} +impl From for CoreSchemaMetaSchemaObjectDependenciesValue { + fn from(value: CoreSchemaMetaSchema) -> Self { + Self::Variant0(value) + } +} +impl From for CoreSchemaMetaSchemaObjectDependenciesValue { + fn from(value: StringArray) -> Self { + Self::Variant1(value) + } +} +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum CoreSchemaMetaSchemaObjectItems { + Variant0(CoreSchemaMetaSchema), + Variant1(SchemaArray), +} +impl From<&CoreSchemaMetaSchemaObjectItems> for CoreSchemaMetaSchemaObjectItems { + fn from(value: &CoreSchemaMetaSchemaObjectItems) -> Self { + value.clone() + } +} +impl Default for CoreSchemaMetaSchemaObjectItems { + fn default() -> Self { + CoreSchemaMetaSchemaObjectItems::Variant0(CoreSchemaMetaSchema::Boolean(true)) + } +} +impl From for CoreSchemaMetaSchemaObjectItems { + fn from(value: CoreSchemaMetaSchema) -> Self { + Self::Variant0(value) + } +} +impl From for CoreSchemaMetaSchemaObjectItems { + fn from(value: SchemaArray) -> Self { + Self::Variant1(value) + } +} +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum CoreSchemaMetaSchemaObjectType { + Variant0(SimpleTypes), + Variant1(Vec), +} +impl From<&CoreSchemaMetaSchemaObjectType> for CoreSchemaMetaSchemaObjectType { + fn from(value: &CoreSchemaMetaSchemaObjectType) -> Self { + value.clone() + } +} +impl From for CoreSchemaMetaSchemaObjectType { + fn from(value: SimpleTypes) -> Self { + Self::Variant0(value) + } +} +impl From> for CoreSchemaMetaSchemaObjectType { + fn from(value: Vec) -> Self { + Self::Variant1(value) + } +} +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct NonNegativeInteger(pub u64); +impl std::ops::Deref for NonNegativeInteger { + type Target = u64; + fn deref(&self) -> &u64 { + &self.0 + } +} +impl From for u64 { + fn from(value: NonNegativeInteger) -> Self { + value.0 + } +} +impl From<&NonNegativeInteger> for NonNegativeInteger { + fn from(value: &NonNegativeInteger) -> Self { + value.clone() + } +} +impl From for NonNegativeInteger { + fn from(value: u64) -> Self { + Self(value) + } +} +impl std::str::FromStr for NonNegativeInteger { + type Err = ::Err; + fn from_str(value: &str) -> Result { + Ok(Self(value.parse()?)) + } +} +impl std::convert::TryFrom<&str> for NonNegativeInteger { + type Error = ::Err; + fn try_from(value: &str) -> Result { + value.parse() + } +} +impl std::convert::TryFrom<&String> for NonNegativeInteger { + type Error = ::Err; + fn try_from(value: &String) -> Result { + value.parse() + } +} +impl std::convert::TryFrom for NonNegativeInteger { + type Error = ::Err; + fn try_from(value: String) -> Result { + value.parse() + } +} +impl ToString for NonNegativeInteger { + fn to_string(&self) -> String { + self.0.to_string() + } +} +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct NonNegativeIntegerDefault0 { + #[serde(flatten)] + pub subtype_0: NonNegativeInteger, + #[serde(flatten)] + pub subtype_1: serde_json::Value, +} +impl From<&NonNegativeIntegerDefault0> for NonNegativeIntegerDefault0 { + fn from(value: &NonNegativeIntegerDefault0) -> Self { + value.clone() + } +} +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct SchemaArray(pub Vec); +impl std::ops::Deref for SchemaArray { + type Target = Vec; + fn deref(&self) -> &Vec { + &self.0 + } +} +impl From for Vec { + fn from(value: SchemaArray) -> Self { + value.0 + } +} +impl From<&SchemaArray> for SchemaArray { + fn from(value: &SchemaArray) -> Self { + value.clone() + } +} +impl From> for SchemaArray { + fn from(value: Vec) -> Self { + Self(value) + } +} +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub enum SimpleTypes { + #[serde(rename = "array")] + Array, + #[serde(rename = "boolean")] + Boolean, + #[serde(rename = "integer")] + Integer, + #[serde(rename = "null")] + Null, + #[serde(rename = "number")] + Number, + #[serde(rename = "object")] + Object, + #[serde(rename = "string")] + String, +} +impl From<&SimpleTypes> for SimpleTypes { + fn from(value: &SimpleTypes) -> Self { + value.clone() + } +} +impl ToString for SimpleTypes { + fn to_string(&self) -> String { + match *self { + Self::Array => "array".to_string(), + Self::Boolean => "boolean".to_string(), + Self::Integer => "integer".to_string(), + Self::Null => "null".to_string(), + Self::Number => "number".to_string(), + Self::Object => "object".to_string(), + Self::String => "string".to_string(), + } + } +} +impl std::str::FromStr for SimpleTypes { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "array" => Ok(Self::Array), + "boolean" => Ok(Self::Boolean), + "integer" => Ok(Self::Integer), + "null" => Ok(Self::Null), + "number" => Ok(Self::Number), + "object" => Ok(Self::Object), + "string" => Ok(Self::String), + _ => Err("invalid value"), + } + } +} +impl std::convert::TryFrom<&str> for SimpleTypes { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } +} +impl std::convert::TryFrom<&String> for SimpleTypes { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } +} +impl std::convert::TryFrom for SimpleTypes { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } +} +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct StringArray(pub Vec); +impl std::ops::Deref for StringArray { + type Target = Vec; + fn deref(&self) -> &Vec { + &self.0 + } +} +impl From for Vec { + fn from(value: StringArray) -> Self { + value.0 + } +} +impl From<&StringArray> for StringArray { + fn from(value: &StringArray) -> Self { + value.clone() + } +} +impl From> for StringArray { + fn from(value: Vec) -> Self { + Self(value) + } +} +pub mod defaults { + pub(super) fn default_bool() -> bool { + V + } + pub(super) fn core_schema_meta_schema_object_items() -> super::CoreSchemaMetaSchemaObjectItems { + super::CoreSchemaMetaSchemaObjectItems::Variant0(super::CoreSchemaMetaSchema::Boolean(true)) + } +} fn main() {} From 3fc6486721245f0a8fcdd59a902fc069a51754d5 Mon Sep 17 00:00:00 2001 From: "Adam H. Leventhal" Date: Thu, 18 May 2023 14:04:27 -0700 Subject: [PATCH 2/4] update --- typify/tests/schemas/reflexive.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/typify/tests/schemas/reflexive.rs b/typify/tests/schemas/reflexive.rs index 35b560b2..f0f96754 100644 --- a/typify/tests/schemas/reflexive.rs +++ b/typify/tests/schemas/reflexive.rs @@ -183,7 +183,7 @@ impl From for CoreSchemaMetaSchemaObjectDependenciesValue { #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(untagged)] pub enum CoreSchemaMetaSchemaObjectItems { - Variant0(CoreSchemaMetaSchema), + Variant0(Box), Variant1(SchemaArray), } impl From<&CoreSchemaMetaSchemaObjectItems> for CoreSchemaMetaSchemaObjectItems { @@ -193,11 +193,11 @@ impl From<&CoreSchemaMetaSchemaObjectItems> for CoreSchemaMetaSchemaObjectItems } impl Default for CoreSchemaMetaSchemaObjectItems { fn default() -> Self { - CoreSchemaMetaSchemaObjectItems::Variant0(CoreSchemaMetaSchema::Boolean(true)) + CoreSchemaMetaSchemaObjectItems::Variant0(Box::new(CoreSchemaMetaSchema::Boolean(true))) } } -impl From for CoreSchemaMetaSchemaObjectItems { - fn from(value: CoreSchemaMetaSchema) -> Self { +impl From> for CoreSchemaMetaSchemaObjectItems { + fn from(value: Box) -> Self { Self::Variant0(value) } } @@ -410,7 +410,9 @@ pub mod defaults { V } pub(super) fn core_schema_meta_schema_object_items() -> super::CoreSchemaMetaSchemaObjectItems { - super::CoreSchemaMetaSchemaObjectItems::Variant0(super::CoreSchemaMetaSchema::Boolean(true)) + super::CoreSchemaMetaSchemaObjectItems::Variant0(Box::new( + super::CoreSchemaMetaSchema::Boolean(true), + )) } } fn main() {} From a3be2a82a4b32b0b8c121857f66cbaa251f219c5 Mon Sep 17 00:00:00 2001 From: "Adam H. Leventhal" Date: Fri, 26 May 2023 18:04:19 -0700 Subject: [PATCH 3/4] remove patch --- typify/tests/schemas/reflexive.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/typify/tests/schemas/reflexive.json b/typify/tests/schemas/reflexive.json index efcdf6ba..605e3eaf 100644 --- a/typify/tests/schemas/reflexive.json +++ b/typify/tests/schemas/reflexive.json @@ -165,8 +165,6 @@ "$ref": "#" }, "propertyNames": { - "$comment": "this is the only change required from the original", - "type": "string", "format": "regex" }, "default": {} From e03da3369ed31fb446350a695f95fb902f698eab Mon Sep 17 00:00:00 2001 From: "Adam H. Leventhal" Date: Fri, 22 Sep 2023 23:32:24 -0700 Subject: [PATCH 4/4] update fixture output --- typify/tests/schemas/reflexive.rs | 50 +++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/typify/tests/schemas/reflexive.rs b/typify/tests/schemas/reflexive.rs index f0f96754..27e6ec03 100644 --- a/typify/tests/schemas/reflexive.rs +++ b/typify/tests/schemas/reflexive.rs @@ -280,17 +280,57 @@ impl ToString for NonNegativeInteger { } } #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct NonNegativeIntegerDefault0 { - #[serde(flatten)] - pub subtype_0: NonNegativeInteger, - #[serde(flatten)] - pub subtype_1: serde_json::Value, +pub struct NonNegativeIntegerDefault0(pub u64); +impl std::ops::Deref for NonNegativeIntegerDefault0 { + type Target = u64; + fn deref(&self) -> &u64 { + &self.0 + } +} +impl From for u64 { + fn from(value: NonNegativeIntegerDefault0) -> Self { + value.0 + } } impl From<&NonNegativeIntegerDefault0> for NonNegativeIntegerDefault0 { fn from(value: &NonNegativeIntegerDefault0) -> Self { value.clone() } } +impl From for NonNegativeIntegerDefault0 { + fn from(value: u64) -> Self { + Self(value) + } +} +impl std::str::FromStr for NonNegativeIntegerDefault0 { + type Err = ::Err; + fn from_str(value: &str) -> Result { + Ok(Self(value.parse()?)) + } +} +impl std::convert::TryFrom<&str> for NonNegativeIntegerDefault0 { + type Error = ::Err; + fn try_from(value: &str) -> Result { + value.parse() + } +} +impl std::convert::TryFrom<&String> for NonNegativeIntegerDefault0 { + type Error = ::Err; + fn try_from(value: &String) -> Result { + value.parse() + } +} +impl std::convert::TryFrom for NonNegativeIntegerDefault0 { + type Error = ::Err; + fn try_from(value: String) -> Result { + value.parse() + } +} +impl ToString for NonNegativeIntegerDefault0 { + fn to_string(&self) -> String { + self.0.to_string() + } +} #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SchemaArray(pub Vec); impl std::ops::Deref for SchemaArray {