Skip to content

fix section schema #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions diagram.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@
"builder": {
"type": "string"
}
},
"additionalProperties": false
}
},
{
"type": "object",
Expand All @@ -157,8 +156,7 @@
"template": {
"type": "string"
}
},
"additionalProperties": false
}
}
],
"required": [
Expand Down
81 changes: 80 additions & 1 deletion src/diagram/section_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

use std::{collections::HashMap, sync::Arc};

use schemars::JsonSchema;
use schemars::{schema::Schema, JsonSchema};
use serde::{Deserialize, Serialize};

use crate::{
Expand All @@ -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::SchemaGenerator) -> Schema {
let mut schema = generator.root_schema_for::<SectionProvider>().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,
Expand Down