diff --git a/.github/workflows/ci_linux.yaml b/.github/workflows/ci_linux.yaml
index 2282199b..da631df2 100644
--- a/.github/workflows/ci_linux.yaml
+++ b/.github/workflows/ci_linux.yaml
@@ -18,7 +18,7 @@ jobs:
build:
strategy:
matrix:
- rust-version: stable
+ rust-version: [stable]
runs-on: ubuntu-latest
diff --git a/diagram.schema.json b/diagram.schema.json
index c7e44ffd..0dbfa57a 100644
--- a/diagram.schema.json
+++ b/diagram.schema.json
@@ -145,8 +145,7 @@
"builder": {
"type": "string"
}
- },
- "additionalProperties": false
+ }
},
{
"type": "object",
@@ -157,8 +156,7 @@
"template": {
"type": "string"
}
- },
- "additionalProperties": false
+ }
}
],
"required": [
diff --git a/src/diagram/section_schema.rs b/src/diagram/section_schema.rs
index 2a2aa5c6..a262fffd 100644
--- a/src/diagram/section_schema.rs
+++ b/src/diagram/section_schema.rs
@@ -17,7 +17,7 @@
use std::{collections::HashMap, sync::Arc};
-use schemars::JsonSchema;
+use schemars::{schema::Schema, JsonSchema};
use serde::{Deserialize, Serialize};
use crate::{
@@ -39,10 +39,89 @@ pub enum SectionProvider {
Template(OperationName),
}
+/// schemars generates schemas with `additionalProperties: false` for enums.
+/// When the enum is flatten, that `additionalProperties: false` is inherited by the parent
+/// struct, which leads to schemas that can never be valid.
+///
+/// Example:
+///
+/// ```json
+/// {
+/// "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>(()) ```",
+/// "type": "object",
+/// "oneOf": [
+/// {
+/// "type": "object",
+/// "required": [
+/// "builder"
+/// ],
+/// "properties": {
+/// "builder": {
+/// "type": "string"
+/// }
+/// },
+/// "additionalProperties": false
+/// },
+/// {
+/// "type": "object",
+/// "required": [
+/// "template"
+/// ],
+/// "properties": {
+/// "template": {
+/// "type": "string"
+/// }
+/// },
+/// "additionalProperties": false
+/// }
+/// ],
+/// "required": [
+/// "type"
+/// ],
+/// "properties": {
+/// "config": {
+/// "default": null
+/// },
+/// "connect": {
+/// "default": {},
+/// "type": "object",
+/// "additionalProperties": {
+/// "$ref": "#/definitions/NextOperation"
+/// }
+/// },
+/// "type": {
+/// "type": "string",
+/// "enum": [
+/// "section"
+/// ]
+/// }
+/// }
+/// },
+/// ```
+///
+/// Here the section schema needs to have a `builder` or `template` with no additional properties.
+/// Which includes other properties like `type`, `config` etc, but `type` is also required which
+/// breaks the schema.
+fn fix_additional_properties(generator: &mut schemars::gen::SchemaGenerator) -> Schema {
+ let mut schema = generator.root_schema_for::().schema;
+ schema.metadata.as_mut().unwrap().title = None;
+ let one_ofs = schema.subschemas.as_mut().unwrap().one_of.as_mut().unwrap();
+ for subschema in one_ofs {
+ match subschema {
+ Schema::Object(schema) => schema.object.as_mut().unwrap().additional_properties = None,
+ _ => {
+ panic!("expected object schema")
+ }
+ }
+ }
+ Schema::Object(schema)
+}
+
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct SectionSchema {
#[serde(flatten)]
+ #[schemars(schema_with = "fix_additional_properties")]
pub(super) provider: SectionProvider,
#[serde(default)]
pub(super) config: serde_json::Value,