From 4ceddbb38abee0908a69cc735b466f459b2ce44a Mon Sep 17 00:00:00 2001 From: Nicolas Dreno Date: Wed, 22 Apr 2026 15:11:25 +0200 Subject: [PATCH] fix: preserve multi-type `type` union when schema has subschemas (#954) When a schema object had both `type: [a, b, c, ...]` and one of `oneOf`/`anyOf`/`allOf`/`not` on the same level, convert_schema_object matched an earlier arm that wildcarded the `instance_type` field (flagged by a pre-existing TODO: "we probably shouldn't ignore this"). The subschema branches were then converted in isolation, silently discarding the outer type union. For the schema in issue #954: { "type": ["string", "number", "boolean", "array"], "oneOf": [ { "items": { "type": "string" } }, { "items": { "type": "number" } }, { "items": { "type": "boolean" } } ] } typify produced an enum with only three array variants, losing the string/number/boolean alternatives entirely. The fix tightens the match pattern to `None | Some(Single(_))`, letting `Some(Vec(_))` fall through to the existing merge arm which already folds the outer schema into each subschema branch via `try_merge_with_subschemas`. Single-type + subschema behaviour is preserved, so tolerant handling of schemas whose outer type conflicts with a branch (e.g. the rust-collisions fixture) is unchanged. Adds a fixture with eight cases covering the affected code path: - `type:[...]` combined with oneOf, anyOf, allOf, and not - explicit `type: array` in every oneOf branch (primitives pruned) - partially and fully unsatisfiable oneOf - simultaneous oneOf + allOf - singleton-type regression guard (rust-collisions pattern) No existing workspace fixture exercised `type: [...]` alongside subschemas on the same object, so this fix had zero pre-existing regression coverage. --- typify-impl/src/convert.rs | 10 +- .../schemas/type-array-with-subschemas.json | 89 ++ .../schemas/type-array-with-subschemas.rs | 933 ++++++++++++++++++ 3 files changed, 1029 insertions(+), 3 deletions(-) create mode 100644 typify/tests/schemas/type-array-with-subschemas.json create mode 100644 typify/tests/schemas/type-array-with-subschemas.rs diff --git a/typify-impl/src/convert.rs b/typify-impl/src/convert.rs index f97b31b9..6dec84e1 100644 --- a/typify-impl/src/convert.rs +++ b/typify-impl/src/convert.rs @@ -464,11 +464,15 @@ impl TypeSpace { extensions: _, } => self.convert_unknown_enum(type_name, original_schema, metadata, enum_values), - // Subschemas + // Subschemas with no body validation and either no type or a + // single type. A multi-type (`Vec`) instance_type is deliberately + // excluded so that it flows through the merge arm below, which + // folds the type union into each subschema branch. Preserving the + // earlier behaviour for `None` / `Single` keeps existing tolerant + // handling of schemas whose outer type may conflict with a branch. SchemaObject { metadata, - // TODO we probably shouldn't ignore this... - instance_type: _, + instance_type: None | Some(SingleOrVec::Single(_)), format: None, enum_values: None, const_value: None, diff --git a/typify/tests/schemas/type-array-with-subschemas.json b/typify/tests/schemas/type-array-with-subschemas.json new file mode 100644 index 00000000..18f26a45 --- /dev/null +++ b/typify/tests/schemas/type-array-with-subschemas.json @@ -0,0 +1,89 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$comment": "Regression coverage for issue #954: schemas with a multi-type `type: [...]` array on the same object as `oneOf` / `anyOf` / `allOf` / `not` previously discarded the `type` constraint (TODO at convert.rs wildcarded `instance_type`). Single-type + subschema cases must still pass through the earlier arm unchanged.", + "definitions": { + "TypeArrayOneOfItems": { + "$comment": "Canonical issue #954 shape. Each oneOf branch only constrains `items`, so the type union must be folded into every branch rather than dropped.", + "type": [ "string", "number", "boolean", "array" ], + "oneOf": [ + { "items": { "type": "string" } }, + { "items": { "type": "number" } }, + { "items": { "type": "boolean" } } + ] + }, + "TypeArrayAnyOfItems": { + "$comment": "Same shape as TypeArrayOneOfItems but using anyOf. anyOf travels through try_merge_with_each_subschema on a sibling path from oneOf; it should fold the type union the same way.", + "type": [ "string", "number", "array" ], + "anyOf": [ + { "items": { "type": "string" } }, + { "items": { "type": "number" } } + ] + }, + "TypeArrayAllOfRefinement": { + "$comment": "allOf is folded pairwise into the parent rather than producing branches. The type union must survive and the array-only constraints should apply when the Array variant is selected.", + "type": [ "string", "array" ], + "allOf": [ + { "items": { "type": "string" } }, + { "minItems": 1 } + ] + }, + "TypeArrayNotExclusion": { + "$comment": "not: object is redundant when the outer type union excludes object, but merging must not drop the type union when the not branch is applied.", + "type": [ "string", "number", "array" ], + "not": { "type": "object" } + }, + "SingleTypeOneOfArrayBranch": { + "$comment": "Regression guard (rust-collisions pattern). Outer singleton type + oneOf where one branch has a conflicting explicit type. This must continue to pass through the earlier arm (no merge), otherwise the array branch becomes unsatisfiable and is silently dropped.", + "type": "object", + "oneOf": [ + { + "type": "object", + "properties": { "kind": { "type": "string" } }, + "required": [ "kind" ] + }, + { + "type": "array", + "items": { "type": "string" }, + "minItems": 2, + "maxItems": 2 + } + ] + }, + "TypeArrayOneOfExplicitArrayBranches": { + "$comment": "Case 7: each oneOf branch pins `type: array`, so the intersection with the outer type union must prune the non-array primitives. Only array variants should be emitted.", + "type": [ "string", "array" ], + "oneOf": [ + { "type": "array", "items": { "type": "string" } }, + { "type": "array", "items": { "type": "number" } } + ] + }, + "TypeArrayPartiallyUnsatisfiableOneOf": { + "$comment": "Some oneOf branches conflict with the outer type union and should be dropped during merge; the surviving branch must carry the outer type union. The two eliminated branches use object/number which the outer `[string, array]` disallows.", + "type": [ "string", "array" ], + "oneOf": [ + { "type": "object", "properties": { "name": { "type": "string" } } }, + { "items": { "type": "string" } }, + { "type": "number" } + ] + }, + "TypeArrayFullyUnsatisfiableOneOf": { + "$comment": "Case 9: every branch conflicts with the outer type union, so `try_merge_with_each_subschema` returns empty and the schema resolves to never. Must emit an empty enum cleanly rather than panic.", + "type": [ "string", "number" ], + "oneOf": [ + { "type": "array", "items": { "type": "string" } }, + { "type": "object", "properties": { "k": { "type": "string" } } } + ] + }, + "TypeArrayOneOfAndAllOf": { + "$comment": "Case 10: oneOf and allOf on the same object, both alongside a multi-type `type` array. Exercises the full merge path (allOf folded first, then oneOf fanned out) with the Vec instance_type flowing through the merge arm.", + "type": [ "string", "array" ], + "allOf": [ + { "minLength": 1 } + ], + "oneOf": [ + { "items": { "type": "string" } }, + { "items": { "type": "number" } } + ] + } + } +} diff --git a/typify/tests/schemas/type-array-with-subschemas.rs b/typify/tests/schemas/type-array-with-subschemas.rs new file mode 100644 index 00000000..7ff669ce --- /dev/null +++ b/typify/tests/schemas/type-array-with-subschemas.rs @@ -0,0 +1,933 @@ +#![deny(warnings)] +#[doc = r" Error types."] +pub mod error { + #[doc = r" 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()) + } + } +} +#[doc = "`SingleTypeOneOfArrayBranch`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": \"object\","] +#[doc = " \"oneOf\": ["] +#[doc = " {"] +#[doc = " \"type\": \"object\","] +#[doc = " \"required\": ["] +#[doc = " \"kind\""] +#[doc = " ],"] +#[doc = " \"properties\": {"] +#[doc = " \"kind\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"type\": \"array\","] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " },"] +#[doc = " \"maxItems\": 2,"] +#[doc = " \"minItems\": 2"] +#[doc = " }"] +#[doc = " ],"] +#[doc = " \"$comment\": \"Regression guard (rust-collisions pattern). Outer singleton type + oneOf where one branch has a conflicting explicit type. This must continue to pass through the earlier arm (no merge), otherwise the array branch becomes unsatisfiable and is silently dropped.\""] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum SingleTypeOneOfArrayBranch { + Object { kind: ::std::string::String }, + Array([::std::string::String; 2usize]), +} +impl ::std::convert::From<[::std::string::String; 2usize]> for SingleTypeOneOfArrayBranch { + fn from(value: [::std::string::String; 2usize]) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayAllOfRefinement`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"minItems\": 1"] +#[doc = " }"] +#[doc = " ],"] +#[doc = " \"$comment\": \"allOf is folded pairwise into the parent rather than producing branches. The type union must survive and the array-only constraints should apply when the Array variant is selected.\""] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayAllOfRefinement { + String(::std::string::String), + Array(::std::vec::Vec<::std::string::String>), +} +impl ::std::convert::From<::std::vec::Vec<::std::string::String>> for TypeArrayAllOfRefinement { + fn from(value: ::std::vec::Vec<::std::string::String>) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayAnyOfItems`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"number\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"anyOf\": ["] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " ],"] +#[doc = " \"$comment\": \"Same shape as TypeArrayOneOfItems but using anyOf. anyOf travels through try_merge_with_each_subschema on a sibling path from oneOf; it should fold the type union the same way.\""] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayAnyOfItems { + Variant0(TypeArrayAnyOfItemsVariant0), + Variant1(TypeArrayAnyOfItemsVariant1), +} +impl ::std::convert::From for TypeArrayAnyOfItems { + fn from(value: TypeArrayAnyOfItemsVariant0) -> Self { + Self::Variant0(value) + } +} +impl ::std::convert::From for TypeArrayAnyOfItems { + fn from(value: TypeArrayAnyOfItemsVariant1) -> Self { + Self::Variant1(value) + } +} +#[doc = "`TypeArrayAnyOfItemsVariant0`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"number\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"$comment\": \"Same shape as TypeArrayOneOfItems but using anyOf. anyOf travels through try_merge_with_each_subschema on a sibling path from oneOf; it should fold the type union the same way.\""] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " }"] +#[doc = " ]"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayAnyOfItemsVariant0 { + Number(f64), + String(::std::string::String), + Array(::std::vec::Vec<::std::string::String>), +} +impl ::std::convert::From for TypeArrayAnyOfItemsVariant0 { + fn from(value: f64) -> Self { + Self::Number(value) + } +} +impl ::std::convert::From<::std::vec::Vec<::std::string::String>> for TypeArrayAnyOfItemsVariant0 { + fn from(value: ::std::vec::Vec<::std::string::String>) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayAnyOfItemsVariant1`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"number\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"$comment\": \"Same shape as TypeArrayOneOfItems but using anyOf. anyOf travels through try_merge_with_each_subschema on a sibling path from oneOf; it should fold the type union the same way.\""] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " }"] +#[doc = " ]"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayAnyOfItemsVariant1 { + Number(f64), + String(::std::string::String), + Array(::std::vec::Vec), +} +impl ::std::convert::From for TypeArrayAnyOfItemsVariant1 { + fn from(value: f64) -> Self { + Self::Number(value) + } +} +impl ::std::convert::From<::std::vec::Vec> for TypeArrayAnyOfItemsVariant1 { + fn from(value: ::std::vec::Vec) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayFullyUnsatisfiableOneOf`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"number\""] +#[doc = " ],"] +#[doc = " \"oneOf\": ["] +#[doc = " {"] +#[doc = " \"type\": \"array\","] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"type\": \"object\","] +#[doc = " \"properties\": {"] +#[doc = " \"k\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " }"] +#[doc = " ],"] +#[doc = " \"$comment\": \"Case 9: every branch conflicts with the outer type union, so `try_merge_with_each_subschema` returns empty and the schema resolves to never. Must emit an empty enum cleanly rather than panic.\""] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive( + :: serde :: Deserialize, + :: serde :: Serialize, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, +)] +#[serde(deny_unknown_fields)] +pub enum TypeArrayFullyUnsatisfiableOneOf {} +#[doc = "`TypeArrayNotExclusion`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"number\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"not\": {"] +#[doc = " \"type\": \"object\""] +#[doc = " },"] +#[doc = " \"$comment\": \"not: object is redundant when the outer type union excludes object, but merging must not drop the type union when the not branch is applied.\""] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayNotExclusion { + Number(f64), + String(::std::string::String), + Array(::std::vec::Vec<::serde_json::Value>), +} +impl ::std::convert::From for TypeArrayNotExclusion { + fn from(value: f64) -> Self { + Self::Number(value) + } +} +impl ::std::convert::From<::std::vec::Vec<::serde_json::Value>> for TypeArrayNotExclusion { + fn from(value: ::std::vec::Vec<::serde_json::Value>) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayOneOfAndAllOf`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"minLength\": 1"] +#[doc = " }"] +#[doc = " ],"] +#[doc = " \"oneOf\": ["] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " ],"] +#[doc = " \"$comment\": \"Case 10: oneOf and allOf on the same object, both alongside a multi-type `type` array. Exercises the full merge path (allOf folded first, then oneOf fanned out) with the Vec instance_type flowing through the merge arm.\""] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayOneOfAndAllOf { + Variant0(TypeArrayOneOfAndAllOfVariant0), + Variant1(TypeArrayOneOfAndAllOfVariant1), +} +impl ::std::convert::From for TypeArrayOneOfAndAllOf { + fn from(value: TypeArrayOneOfAndAllOfVariant0) -> Self { + Self::Variant0(value) + } +} +impl ::std::convert::From for TypeArrayOneOfAndAllOf { + fn from(value: TypeArrayOneOfAndAllOfVariant1) -> Self { + Self::Variant1(value) + } +} +#[doc = "`TypeArrayOneOfAndAllOfVariant0`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"minLength\": 1"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " }"] +#[doc = " ]"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayOneOfAndAllOfVariant0 { + String(TypeArrayOneOfAndAllOfVariant0String), + Array(::std::vec::Vec<::std::string::String>), +} +impl ::std::convert::From for TypeArrayOneOfAndAllOfVariant0 { + fn from(value: TypeArrayOneOfAndAllOfVariant0String) -> Self { + Self::String(value) + } +} +impl ::std::convert::From<::std::vec::Vec<::std::string::String>> + for TypeArrayOneOfAndAllOfVariant0 +{ + fn from(value: ::std::vec::Vec<::std::string::String>) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayOneOfAndAllOfVariant0String`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": \"string\","] +#[doc = " \"minLength\": 1"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[serde(transparent)] +pub struct TypeArrayOneOfAndAllOfVariant0String(::std::string::String); +impl ::std::ops::Deref for TypeArrayOneOfAndAllOfVariant0String { + type Target = ::std::string::String; + fn deref(&self) -> &::std::string::String { + &self.0 + } +} +impl ::std::convert::From for ::std::string::String { + fn from(value: TypeArrayOneOfAndAllOfVariant0String) -> Self { + value.0 + } +} +impl ::std::str::FromStr for TypeArrayOneOfAndAllOfVariant0String { + type Err = self::error::ConversionError; + fn from_str(value: &str) -> ::std::result::Result { + if value.chars().count() < 1usize { + return Err("shorter than 1 characters".into()); + } + Ok(Self(value.to_string())) + } +} +impl ::std::convert::TryFrom<&str> for TypeArrayOneOfAndAllOfVariant0String { + type Error = self::error::ConversionError; + fn try_from(value: &str) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<&::std::string::String> for TypeArrayOneOfAndAllOfVariant0String { + type Error = self::error::ConversionError; + fn try_from( + value: &::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for TypeArrayOneOfAndAllOfVariant0String { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +impl<'de> ::serde::Deserialize<'de> for TypeArrayOneOfAndAllOfVariant0String { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: ::serde::Deserializer<'de>, + { + ::std::string::String::deserialize(deserializer)? + .parse() + .map_err(|e: self::error::ConversionError| { + ::custom(e.to_string()) + }) + } +} +#[doc = "`TypeArrayOneOfAndAllOfVariant1`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"minLength\": 1"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " }"] +#[doc = " ]"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayOneOfAndAllOfVariant1 { + String(TypeArrayOneOfAndAllOfVariant1String), + Array(::std::vec::Vec), +} +impl ::std::convert::From for TypeArrayOneOfAndAllOfVariant1 { + fn from(value: TypeArrayOneOfAndAllOfVariant1String) -> Self { + Self::String(value) + } +} +impl ::std::convert::From<::std::vec::Vec> for TypeArrayOneOfAndAllOfVariant1 { + fn from(value: ::std::vec::Vec) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayOneOfAndAllOfVariant1String`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": \"string\","] +#[doc = " \"minLength\": 1"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[serde(transparent)] +pub struct TypeArrayOneOfAndAllOfVariant1String(::std::string::String); +impl ::std::ops::Deref for TypeArrayOneOfAndAllOfVariant1String { + type Target = ::std::string::String; + fn deref(&self) -> &::std::string::String { + &self.0 + } +} +impl ::std::convert::From for ::std::string::String { + fn from(value: TypeArrayOneOfAndAllOfVariant1String) -> Self { + value.0 + } +} +impl ::std::str::FromStr for TypeArrayOneOfAndAllOfVariant1String { + type Err = self::error::ConversionError; + fn from_str(value: &str) -> ::std::result::Result { + if value.chars().count() < 1usize { + return Err("shorter than 1 characters".into()); + } + Ok(Self(value.to_string())) + } +} +impl ::std::convert::TryFrom<&str> for TypeArrayOneOfAndAllOfVariant1String { + type Error = self::error::ConversionError; + fn try_from(value: &str) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<&::std::string::String> for TypeArrayOneOfAndAllOfVariant1String { + type Error = self::error::ConversionError; + fn try_from( + value: &::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +impl ::std::convert::TryFrom<::std::string::String> for TypeArrayOneOfAndAllOfVariant1String { + type Error = self::error::ConversionError; + fn try_from( + value: ::std::string::String, + ) -> ::std::result::Result { + value.parse() + } +} +impl<'de> ::serde::Deserialize<'de> for TypeArrayOneOfAndAllOfVariant1String { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: ::serde::Deserializer<'de>, + { + ::std::string::String::deserialize(deserializer)? + .parse() + .map_err(|e: self::error::ConversionError| { + ::custom(e.to_string()) + }) + } +} +#[doc = "`TypeArrayOneOfExplicitArrayBranches`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"oneOf\": ["] +#[doc = " {"] +#[doc = " \"type\": \"array\","] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"type\": \"array\","] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " ],"] +#[doc = " \"$comment\": \"Case 7: each oneOf branch pins `type: array`, so the intersection with the outer type union must prune the non-array primitives. Only array variants should be emitted.\""] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayOneOfExplicitArrayBranches { + Variant0(::std::vec::Vec<::std::string::String>), + Variant1(::std::vec::Vec), +} +impl ::std::convert::From<::std::vec::Vec<::std::string::String>> + for TypeArrayOneOfExplicitArrayBranches +{ + fn from(value: ::std::vec::Vec<::std::string::String>) -> Self { + Self::Variant0(value) + } +} +impl ::std::convert::From<::std::vec::Vec> for TypeArrayOneOfExplicitArrayBranches { + fn from(value: ::std::vec::Vec) -> Self { + Self::Variant1(value) + } +} +#[doc = "`TypeArrayOneOfItems`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"number\","] +#[doc = " \"boolean\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"oneOf\": ["] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"boolean\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " ],"] +#[doc = " \"$comment\": \"Canonical issue #954 shape. Each oneOf branch only constrains `items`, so the type union must be folded into every branch rather than dropped.\""] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayOneOfItems { + Variant0(TypeArrayOneOfItemsVariant0), + Variant1(TypeArrayOneOfItemsVariant1), + Variant2(TypeArrayOneOfItemsVariant2), +} +impl ::std::convert::From for TypeArrayOneOfItems { + fn from(value: TypeArrayOneOfItemsVariant0) -> Self { + Self::Variant0(value) + } +} +impl ::std::convert::From for TypeArrayOneOfItems { + fn from(value: TypeArrayOneOfItemsVariant1) -> Self { + Self::Variant1(value) + } +} +impl ::std::convert::From for TypeArrayOneOfItems { + fn from(value: TypeArrayOneOfItemsVariant2) -> Self { + Self::Variant2(value) + } +} +#[doc = "`TypeArrayOneOfItemsVariant0`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"number\","] +#[doc = " \"boolean\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"$comment\": \"Canonical issue #954 shape. Each oneOf branch only constrains `items`, so the type union must be folded into every branch rather than dropped.\""] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"boolean\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " }"] +#[doc = " ]"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayOneOfItemsVariant0 { + Boolean(bool), + Number(f64), + String(::std::string::String), + Array(::std::vec::Vec<::std::string::String>), +} +impl ::std::convert::From for TypeArrayOneOfItemsVariant0 { + fn from(value: bool) -> Self { + Self::Boolean(value) + } +} +impl ::std::convert::From for TypeArrayOneOfItemsVariant0 { + fn from(value: f64) -> Self { + Self::Number(value) + } +} +impl ::std::convert::From<::std::vec::Vec<::std::string::String>> for TypeArrayOneOfItemsVariant0 { + fn from(value: ::std::vec::Vec<::std::string::String>) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayOneOfItemsVariant1`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"number\","] +#[doc = " \"boolean\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"$comment\": \"Canonical issue #954 shape. Each oneOf branch only constrains `items`, so the type union must be folded into every branch rather than dropped.\""] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"boolean\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " }"] +#[doc = " ]"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayOneOfItemsVariant1 { + Boolean(bool), + Number(f64), + String(::std::string::String), + Array(::std::vec::Vec), +} +impl ::std::convert::From for TypeArrayOneOfItemsVariant1 { + fn from(value: bool) -> Self { + Self::Boolean(value) + } +} +impl ::std::convert::From for TypeArrayOneOfItemsVariant1 { + fn from(value: f64) -> Self { + Self::Number(value) + } +} +impl ::std::convert::From<::std::vec::Vec> for TypeArrayOneOfItemsVariant1 { + fn from(value: ::std::vec::Vec) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayOneOfItemsVariant2`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"number\","] +#[doc = " \"boolean\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"$comment\": \"Canonical issue #954 shape. Each oneOf branch only constrains `items`, so the type union must be folded into every branch rather than dropped.\""] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"boolean\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"not\": {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " }"] +#[doc = " ]"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayOneOfItemsVariant2 { + Boolean(bool), + Number(f64), + String(::std::string::String), + Array(::std::vec::Vec), +} +impl ::std::convert::From for TypeArrayOneOfItemsVariant2 { + fn from(value: bool) -> Self { + Self::Boolean(value) + } +} +impl ::std::convert::From for TypeArrayOneOfItemsVariant2 { + fn from(value: f64) -> Self { + Self::Number(value) + } +} +impl ::std::convert::From<::std::vec::Vec> for TypeArrayOneOfItemsVariant2 { + fn from(value: ::std::vec::Vec) -> Self { + Self::Array(value) + } +} +#[doc = "`TypeArrayPartiallyUnsatisfiableOneOf`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"array\""] +#[doc = " ],"] +#[doc = " \"oneOf\": ["] +#[doc = " {"] +#[doc = " \"type\": \"object\","] +#[doc = " \"properties\": {"] +#[doc = " \"name\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"items\": {"] +#[doc = " \"type\": \"string\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " {"] +#[doc = " \"type\": \"number\""] +#[doc = " }"] +#[doc = " ],"] +#[doc = " \"$comment\": \"Some oneOf branches conflict with the outer type union and should be dropped during merge; the surviving branch must carry the outer type union. The two eliminated branches use object/number which the outer `[string, array]` disallows.\""] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum TypeArrayPartiallyUnsatisfiableOneOf { + String(::std::string::String), + Array(::std::vec::Vec<::std::string::String>), +} +impl ::std::convert::From<::std::vec::Vec<::std::string::String>> + for TypeArrayPartiallyUnsatisfiableOneOf +{ + fn from(value: ::std::vec::Vec<::std::string::String>) -> Self { + Self::Array(value) + } +} +fn main() {}