Issue with schema props
Opened this issue · 8 comments
.1.0 (C:\Users\miner\OneDrive\Documents\GitHub\Androecia\FriendConnect-rs\minecraft-bedrock-schemas-rs)`
Caused by:
process didn't exit successfully: C:\Users\miner\OneDrive\Documents\GitHub\Androecia\FriendConnect-rs\minecraft-bedrock-schemas-rs\target\debug\build\minecraft-bedrock-schemas-89cec1978fa2cbc3\build-script-build (exit code: 101)
--- stdout
Writing to: .\src\behavior\animations\animations.rs
Writing to: .\src\behavior\animation_controllers\animation_controller.rs
--- stderr
thread 'main' panicked at 'not yet implemented: invalid (or unexpected) schema:
SchemaObject {
metadata: Some(
Metadata {
id: None,
title: Some(
"Transition",
),
description: Some(
"The transition definition for.",
),
default: None,
deprecated: false,
read_only: false,
write_only: false,
examples: [],
},
),
instance_type: Some(
Single(
Array,
),
),
format: None,
enum_values: None,
const_value: None,
subschemas: None,
number: None,
string: None,
array: Some(
ArrayValidation {
items: Some(
Single(
Object(
SchemaObject {
metadata: Some(
Metadata {
id: None,
title: Some(
"Transition",
),
description: Some(
"A transition to another state.",
),
default: None,
deprecated: false,
read_only: false,
write_only: false,
examples: [
Object {
"default": String("query.is_chested"),
},
],
},
),
instance_type: Some(
Single(
Object,
),
),
format: None,
enum_values: None,
const_value: None,
subschemas: None,
number: None,
string: None,
array: None,
object: Some(
ObjectValidation {
max_properties: Some(
1,
),
min_properties: Some(
1,
),
required: {},
properties: {},
pattern_properties: {},
additional_properties: Some(
Object(
SchemaObject {
metadata: None,
instance_type: None,
format: None,
enum_values: None,
const_value: None,
subschemas: None,
number: None,
string: None,
array: None,
object: None,
reference: Some(
"#/definitions/A",
),
extensions: {},
},
),
),
property_names: None,
},
),
reference: None,
extensions: {},
},
),
),
),
additional_items: None,
max_items: None,
min_items: None,
unique_items: None,
contains: None,
},
),
object: Some(
ObjectValidation {
max_properties: None,
min_properties: Some(
1,
),
required: {},
properties: {},
pattern_properties: {},
additional_properties: None,
property_names: None,
},
),
reference: None,
extensions: {},
}', typify\typify-impl\src\convert.rs:551:36
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "blockception.minecraft.behavior.animation_controller",
"examples": [
{
"format_version": "1.19.0",
"animation_controllers": {
"controller.animation.example": {
"initial_state": "default",
"states": {
"default": {
"transitions": [
{
"state_1": "query.is_baby"
}
]
},
"state_1": {}
}
}
}
}
],
"definitions": {
"animationspec": {
"anyOf": [
{
"title": "Animation Specification",
"description": "A single string that specifies which animation there are.",
"type": "string"
},
{
"type": "object",
"title": "Animation Specification",
"description": "A object specification on when to animate.",
"maxProperties": 1,
"minProperties": 1,
"additionalProperties": {
"$ref": "#/definitions/A"
}
}
]
},
"particle_effect_spec": {
"additionalProperties": false,
"type": "object",
"required": [
"effect"
],
"properties": {
"bind_to_actor": {
"type": "boolean",
"title": "Bind To Actor",
"description": "Set to false to have the effect spawned in the world without being bound to an actor (by default an effect is bound to the actor).",
"const": false
},
"effect": {
"type": "string",
"title": "Effect",
"description": "The name of a particle effect that should be played."
},
"locator": {
"type": "string",
"title": "Locator",
"description": "The name of a locator on the actor where the effect should be located."
},
"pre_effect_script": {
"type": "string",
"title": "Pre Effect Script",
"description": "A molang script that will be run when the particle emitter is initialized."
}
}
},
"commands": {
"type": "string",
"description": "The event or commands to execute.",
"title": "Commands",
"oneOf": [
{
"pattern": "^@s .+$",
"title": "Event"
},
{
"pattern": "^/.+$",
"title": "Command"
},
{
"pattern": "^.+;$",
"title": "Molang"
}
]
},
"A": {
"type": "string",
"title": "Molang",
"description": "Molang definition.",
"format": "molang",
"examples": [
"query.variant",
"(1.0)",
"query.",
"variable.=;"
],
"defaultSnippets": [
{
"label": "New Molang",
"body": "$1"
}
]
},
"B": {
"title": "Format Version",
"description": "A version that tells minecraft what type of data format can be expected when reading this file.",
"pattern": "^([1-9]+)\\.([0-9]+)\\.([0-9]+)$",
"type": "string",
"default": "1.19.40",
"examples": [
"1.19.40",
"1.18.0",
"1.17.0",
"1.16.0",
"1.15.0",
"1.14.0",
"1.13.0",
"1.12.0",
"1.10.0",
"1.8.0"
],
"defaultSnippets": [
{
"label": "New Format version",
"body": "1.${1|8,10,12,17,18,19|}.${3|2|0|}"
}
]
}
},
"type": "object",
"title": "Animation Controller",
"description": "Animation controller for behaviors.",
"required": [
"format_version",
"animation_controllers"
],
"additionalProperties": false,
"properties": {
"format_version": {
"$ref": "#/definitions/B"
},
"animation_controllers": {
"type": "object",
"title": "Animation Controllers",
"description": "The animation controllers schema for.",
"propertyNames": {
"pattern": "^controller\\.animation\\.[a-z\\.]+",
"examples": [
"controller.animation.example",
"controller.animation.example.foo"
]
},
"additionalProperties": {
"additionalProperties": false,
"type": "object",
"title": "Animation Controller",
"description": "A single animation controller.",
"required": [
"states"
],
"minProperties": 1,
"properties": {
"states": {
"title": "States",
"description": "The states of this animation controller.",
"propertyNames": {
"pattern": "[a-z\\.]+"
},
"minProperties": 1,
"type": "object",
"additionalProperties": {
"additionalProperties": false,
"title": "Animation State",
"description": "Animation state.",
"type": "object",
"examples": [
{
"animations": [
"anim.idle"
],
"transitions": [
{
"example": "query.is_sheared"
}
]
}
],
"properties": {
"animations": {
"title": "Animations",
"description": "The animations definition for.",
"type": "array",
"items": {
"$ref": "#/definitions/animationspec",
"description": "The key definition of an animation to play, defined in the entity.",
"title": "Animations"
}
},
"on_entry": {
"type": "array",
"description": "Events, commands or transitions to preform on entry of this state.",
"title": "On Entry",
"items": {
"$ref": "#/definitions/commands"
}
},
"on_exit": {
"type": "array",
"description": "Events, commands or transitions to preform on exit of this state.",
"title": "On Exit",
"items": {
"$ref": "#/definitions/commands"
}
},
"transitions": {
"title": "Transition",
"description": "The transition definition for.",
"minProperties": 1,
"type": "array",
"items": {
"title": "Transition",
"description": "A transition to another state.",
"type": "object",
"maxProperties": 1,
"minProperties": 1,
"examples": [
{
"default": "query.is_chested"
}
],
"additionalProperties": {
"$ref": "#/definitions/A"
}
}
}
}
}
},
"initial_state": {
"title": "Initial State",
"description": "The state to start with, if not specified state at position 0 in the array is used.",
"type": "string",
"examples": [
"default"
]
}
}
}
}
}
}
Also wouldn't it be a better approach to use an enum for anyOf
The "Transitions" schema seems to contain an error:
"transitions": {
"title": "Transition",
"description": "The transition definition for.",
"minProperties": 1,
"type": "array",
...
This is an array
with minProperties
which is only valid for object
types. I think it should be minItems
: https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-02#section-6.4.2
As it stands I believe that this schema is unsatisfiable in the most pedantic sense. That is to say, a type can't both be an array and have at least one object property.
It's hard to know what to do with these kinds of mis-formed schemas. We could try to infer what the intent was, but that's a slippery slope that can lead to bad assumptions. I recognize that this is a JSON schema whose content you don't control. Typify could do more to explain the problem (e.g. "object validation on array
type"); then I'd suggest you could use something like JSON patch to clean it up, or a more general "JSON schema cleaner" to look for mistakes such as this.
The only path forward I can see here that we could handle is to ignore fields that are inconsistent with the type
field.
While looking into #230 it does look like it's reasonable--maybe even correct--to ignore constraints unrelated to the type
. I'll implement that when I have the chance.
While looking into #230 it does look like it's reasonable--maybe even correct--to ignore constraints unrelated to the
type
. I'll implement that when I have the chance.
Looking into this later I have validated the schema and it did not error out so the schema is correct according to draft 7
{
"$id": "blockception.minecraft.language_names",
"$schema": "http://json-schema.org/draft-07/schema",
"additionalProperties": false,
"definitions": {},
"description": "A language names definitions file.",
"examples": [
[
[
"en_US",
"English (US)"
],
[
"en_GB",
"English (UK)"
],
[
"de_DE",
"Deutsch (Deutschland)"
],
[
"es_ES",
"Español (España)"
],
[
"es_MX",
"Español (México)"
],
[
"fr_FR",
"Français (France)"
],
[
"fr_CA",
"Français (Canada)"
],
[
"it_IT",
"Italiano (Italia)"
],
[
"ja_JP",
"日本語 (日本)"
],
[
"ko_KR",
"한국어 (대한민국)"
],
[
"pt_BR",
"Português (Brasil)"
],
[
"pt_PT",
"Português (Portugal)"
],
[
"ru_RU",
"Русский (Россия)"
],
[
"zh_CN",
"简体中文"
],
[
"zh_TW",
"繁體中文"
],
[
"nl_NL",
"Nederlands (Nederland)"
],
[
"bg_BG",
"Български (BG)"
],
[
"cs_CZ",
"Čeština (Česká republika)"
],
[
"da_DK",
"Dansk (DA)"
],
[
"el_GR",
"Ελληνικά (Ελλάδα)"
],
[
"fi_FI",
"Suomi (Suomi)"
],
[
"hu_HU",
"Magyar (HU)"
],
[
"id_ID",
"Bahasa Indonesia (Indonesia)"
],
[
"nb_NO",
"Norsk bokmål (Norge)"
],
[
"pl_PL",
"Polski (PL)"
],
[
"sk_SK",
"Slovensky (SK)"
],
[
"sv_SE",
"Svenska (Sverige)"
],
[
"tr_TR",
"Türkçe (Türkiye)"
],
[
"uk_UA",
"Українська (Україна)"
]
]
],
"items": {
"description": "A language name identifier.",
"items": [
{
"description": "A language identifier.",
"pattern": "^[a-z]{2}_[A-Z]{2}$",
"type": "string"
},
{
"description": "The name of the language.",
"type": "string"
}
],
"type": "array"
},
"title": "Language Names",
"type": "array"
}
I dont seem to understand why this errors out, this is something when I have run into.
The first schema you mentioned should be addressed by #255 which fixes #253. The second schema you mentioned is more complex. If it had
"minItems": 2,
"maxItems": 2,
... it would be pretty simple to model as Vec<(StringWithRegex, String)>
but because it doesn't, we need a type more like this:
struct ArrayItem(Option<StringWithRegex>, Option<String>, Vec<serde_json::Value>);
The inner array schema validates with an array of any number of items as long as the first two (if present) conform the string-with-regex, and string respectively. I've opened #254 to track that work more specifically.
Or maybe something like this--assuming we would prefer that only valid states are representable in Rust:
enum ArrayItem {
pub Items0,
pub Items1(StringWithRegex),
pub Items2(StringWithRegex, String),
pub ItemsN(StringWithRegex, String, Vec<serde_json::Value>),
}
impl ArrayItem {
pub fn get0(&self) -> Option<&StringWithRegex> {
match self {
Self::Items1(zero) | Self::Items2(zero, ..) | Self::ItemsN(zero, ..) => Some(zero),
_ => None,
}
}
pub fn get1(&self) -> Option<&StringWithRegex> {
match self {
Self::Items2(_, one) | Self::ItemsN(_, one, ..) => Some(one),
_ => None,
}
}
// ...
}
We could generate something to make it easier to construct:
mod builder {
struct ArrayItem(Option<StringWithRegex>, Option<String>, Vec<serde_json::Value>);
pub fn with_0(self, item_0: StringWithRegex) -> Self {
let (_, item_1, rest) = self;
Self(Some(item_0), item_1, rest)
}
// ...
impl std::convert::TryFrom<ArrayItem> for super::ArrayItem {
fn try_from(value: ArrayItem) -> Result<super::ArrayItem, String> {
match self {
(None, None, rest) if rest.is_empty() => Ok(Self::Items0),
(Some(zero), None, rest) if rest.is_empty() => Ok(Self::Items1(zero)),
(Some(zero), Some(one), rest) if rest.is_empty() => Ok(Self::Items2(zero, one)),
(Some(zero), Some(one), rest) => Ok(Self::ItemsN(zero, one, rest)),
_ => Err("nope".to_string()),
}
}
}
}