17
17
18
18
use std:: { collections:: HashMap , sync:: Arc } ;
19
19
20
- use schemars:: JsonSchema ;
20
+ use schemars:: { schema :: Schema , JsonSchema } ;
21
21
use serde:: { Deserialize , Serialize } ;
22
22
23
23
use crate :: {
@@ -39,10 +39,89 @@ pub enum SectionProvider {
39
39
Template ( OperationName ) ,
40
40
}
41
41
42
+ /// schemars generates schemas with `additionalProperties: false` for enums.
43
+ /// When the enum is flatten, that `additionalProperties: false` is inherited by the parent
44
+ /// struct, which leads to schemas that can never be valid.
45
+ ///
46
+ /// Example:
47
+ ///
48
+ /// ```json
49
+ /// {
50
+ /// "description": "Connect the request to a registered section.\n\n``` # bevy_impulse::Diagram::from_json_str(r#\" { \"version\": \"0.1.0\", \"start\": \"section_op\", \"ops\": { \"section_op\": { \"type\": \"section\", \"builder\": \"my_section_builder\", \"connect\": { \"my_section_output\": { \"builtin\": \"terminate\" } } } } } # \"#)?; # Ok::<_, serde_json::Error>(()) ```\n\nCustom sections can also be created via templates ``` # bevy_impulse::Diagram::from_json_str(r#\" { \"version\": \"0.1.0\", \"templates\": { \"my_template\": { \"inputs\": [\"section_input\"], \"outputs\": [\"section_output\"], \"buffers\": [], \"ops\": { \"section_input\": { \"type\": \"node\", \"builder\": \"my_node\", \"next\": \"section_output\" } } } }, \"start\": \"section_op\", \"ops\": { \"section_op\": { \"type\": \"section\", \"template\": \"my_template\", \"connect\": { \"section_output\": { \"builtin\": \"terminate\" } } } } } # \"#)?; # Ok::<_, serde_json::Error>(()) ```",
51
+ /// "type": "object",
52
+ /// "oneOf": [
53
+ /// {
54
+ /// "type": "object",
55
+ /// "required": [
56
+ /// "builder"
57
+ /// ],
58
+ /// "properties": {
59
+ /// "builder": {
60
+ /// "type": "string"
61
+ /// }
62
+ /// },
63
+ /// "additionalProperties": false
64
+ /// },
65
+ /// {
66
+ /// "type": "object",
67
+ /// "required": [
68
+ /// "template"
69
+ /// ],
70
+ /// "properties": {
71
+ /// "template": {
72
+ /// "type": "string"
73
+ /// }
74
+ /// },
75
+ /// "additionalProperties": false
76
+ /// }
77
+ /// ],
78
+ /// "required": [
79
+ /// "type"
80
+ /// ],
81
+ /// "properties": {
82
+ /// "config": {
83
+ /// "default": null
84
+ /// },
85
+ /// "connect": {
86
+ /// "default": {},
87
+ /// "type": "object",
88
+ /// "additionalProperties": {
89
+ /// "$ref": "#/definitions/NextOperation"
90
+ /// }
91
+ /// },
92
+ /// "type": {
93
+ /// "type": "string",
94
+ /// "enum": [
95
+ /// "section"
96
+ /// ]
97
+ /// }
98
+ /// }
99
+ /// },
100
+ /// ```
101
+ ///
102
+ /// Here the section schema needs to have a `builder` or `template` with no additional properties.
103
+ /// Which includes other properties like `type`, `config` etc, but `type` is also required which
104
+ /// breaks the schema.
105
+ fn fix_additional_properties ( generator : & mut schemars:: gen:: SchemaGenerator ) -> Schema {
106
+ let mut schema = generator. root_schema_for :: < SectionProvider > ( ) . schema ;
107
+ schema. metadata . as_mut ( ) . unwrap ( ) . title = None ;
108
+ let one_ofs = schema. subschemas . as_mut ( ) . unwrap ( ) . one_of . as_mut ( ) . unwrap ( ) ;
109
+ for subschema in one_ofs {
110
+ match subschema {
111
+ Schema :: Object ( schema) => schema. object . as_mut ( ) . unwrap ( ) . additional_properties = None ,
112
+ _ => {
113
+ panic ! ( "expected object schema" )
114
+ }
115
+ }
116
+ }
117
+ Schema :: Object ( schema)
118
+ }
119
+
42
120
#[ derive( Clone , Debug , Serialize , Deserialize , JsonSchema ) ]
43
121
#[ serde( rename_all = "snake_case" ) ]
44
122
pub struct SectionSchema {
45
123
#[ serde( flatten) ]
124
+ #[ schemars( schema_with = "fix_additional_properties" ) ]
46
125
pub ( super ) provider : SectionProvider ,
47
126
#[ serde( default ) ]
48
127
pub ( super ) config : serde_json:: Value ,
0 commit comments