diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 678ee41..1c6fd88 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -75,7 +75,7 @@ impl SchemaSettings { SchemaSettings { option_nullable: false, option_add_null_type: true, - definitions_path: "#/definitions/".to_owned(), + definitions_path: "#/$defs/".to_owned(), meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()), visitors: Vec::new(), inline_subschemas: false, @@ -253,7 +253,8 @@ impl SchemaGenerator { // insert into definitions BEFORE calling json_schema to avoid infinite recursion self.definitions.insert(name.clone(), dummy); - let schema = self.json_schema_internal::(id); + let mut schema = self.json_schema_internal::(id); + Self::run_visitors(&mut schema, &mut self.settings.visitors); self.definitions.insert(name, schema.to_value()); } @@ -306,16 +307,12 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - if !self.definitions.is_empty() { - object.insert( - "definitions".into(), - serde_json::Value::Object(self.definitions.clone()), - ); - } - - for visitor in &mut self.settings.visitors { - visitor.visit_schema(&mut schema); - } + Self::add_definitions( + object, + self.definitions.clone(), + &self.settings.definitions_path, + ); + Self::run_visitors(&mut schema, &mut self.settings.visitors); schema } @@ -337,16 +334,8 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - if !self.definitions.is_empty() { - object.insert( - "definitions".into(), - serde_json::Value::Object(self.definitions), - ); - } - - for visitor in &mut self.settings.visitors { - visitor.visit_schema(&mut schema); - } + Self::add_definitions(object, self.definitions, &self.settings.definitions_path); + Self::run_visitors(&mut schema, &mut self.settings.visitors); schema } @@ -374,16 +363,12 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - if !self.definitions.is_empty() { - object.insert( - "definitions".into(), - serde_json::Value::Object(self.definitions.clone()), - ); - } - - for visitor in &mut self.settings.visitors { - visitor.visit_schema(&mut schema); - } + Self::add_definitions( + object, + self.definitions.clone(), + &self.settings.definitions_path, + ); + Self::run_visitors(&mut schema, &mut self.settings.visitors); Ok(schema) } @@ -411,16 +396,8 @@ impl SchemaGenerator { object.insert("$schema".into(), meta_schema.into()); } - if !self.definitions.is_empty() { - object.insert( - "definitions".into(), - serde_json::Value::Object(self.definitions), - ); - } - - for visitor in &mut self.settings.visitors { - visitor.visit_schema(&mut schema); - } + Self::add_definitions(object, self.definitions, &self.settings.definitions_path); + Self::run_visitors(&mut schema, &mut self.settings.visitors); Ok(schema) } @@ -450,6 +427,51 @@ impl SchemaGenerator { let pss = PendingSchemaState::new(self, id); T::json_schema(pss.gen) } + + fn add_definitions( + schema_object: &mut Map, + mut definitions: Map, + path: &str, + ) { + if definitions.is_empty() { + return; + } + + let target = match Self::json_pointer(schema_object, path) { + Some(d) => d, + None => return, + }; + + target.append(&mut definitions); + } + + fn json_pointer<'a>( + mut object: &'a mut Map, + pointer: &str, + ) -> Option<&'a mut Map> { + let segments = pointer.strip_prefix("#/")?.strip_suffix('/')?.split('/'); + + for mut segment in segments { + let replaced: String; + if segment.contains('~') { + replaced = segment.replace("~1", "/").replace("~0", "~"); + segment = &replaced; + } + + object = object + .entry(segment) + .or_insert(Value::Object(Map::default())) + .as_object_mut()?; + } + + Some(object) + } + + fn run_visitors(schema: &mut Schema, visitors: &mut [Box]) { + for visitor in visitors { + visitor.visit_schema(schema); + } + } } /// A [Visitor](Visitor) which implements additional traits required to be included in a [SchemaSettings]. diff --git a/schemars/src/visit.rs b/schemars/src/visit.rs index 5fd4273..a9ca787 100644 --- a/schemars/src/visit.rs +++ b/schemars/src/visit.rs @@ -78,7 +78,7 @@ pub fn visit_schema(v: &mut V, schema: &mut Schema) { v.visit_schema(subschema) } } - "properties" | "patternProperties" | "definitions" | "$defs" => { + "properties" | "patternProperties" => { if let Some(obj) = value.as_object_mut() { for value in obj.values_mut() { if let Ok(subschema) = value.try_into() { diff --git a/schemars/tests/expected/doc_comments_struct_ref_siblings.json b/schemars/tests/expected/doc_comments_struct_ref_siblings.json index a3dcbe0..6b6caeb 100644 --- a/schemars/tests/expected/doc_comments_struct_ref_siblings.json +++ b/schemars/tests/expected/doc_comments_struct_ref_siblings.json @@ -14,7 +14,7 @@ }, "my_unit": { "description": "A unit struct instance", - "$ref": "#/definitions/MyUnitStruct" + "$ref": "#/$defs/MyUnitStruct" } }, "required": [ @@ -22,7 +22,7 @@ "my_undocumented_bool", "my_unit" ], - "definitions": { + "$defs": { "MyUnitStruct": { "title": "A Unit", "type": "null" diff --git a/schemars/tests/expected/schema_settings-2019_09.json b/schemars/tests/expected/schema_settings-2019_09.json index 22f962f..568ce85 100644 --- a/schemars/tests/expected/schema_settings-2019_09.json +++ b/schemars/tests/expected/schema_settings-2019_09.json @@ -19,7 +19,7 @@ "inner": { "anyOf": [ { - "$ref": "#/definitions/Inner" + "$ref": "#/$defs/Inner" }, { "type": "null" @@ -32,7 +32,7 @@ "values", "value" ], - "definitions": { + "$defs": { "Inner": { "oneOf": [ { diff --git a/schemars/tests/expected/schema_settings-openapi3.json b/schemars/tests/expected/schema_settings-openapi3.json index c4e5199..6ff2604 100644 --- a/schemars/tests/expected/schema_settings-openapi3.json +++ b/schemars/tests/expected/schema_settings-openapi3.json @@ -27,34 +27,36 @@ "values", "value" ], - "definitions": { - "Inner": { - "oneOf": [ - { - "type": "string", - "enum": [ - "UndocumentedUnit1", - "UndocumentedUnit2" - ] - }, - { - "description": "This is a documented unit variant", - "type": "string", - "enum": [ - "DocumentedUnit" - ] - }, - { - "type": "object", - "properties": { - "ValueNewType": {} + "components": { + "schemas": { + "Inner": { + "oneOf": [ + { + "type": "string", + "enum": [ + "UndocumentedUnit1", + "UndocumentedUnit2" + ] }, - "required": [ - "ValueNewType" - ], - "additionalProperties": false - } - ] + { + "description": "This is a documented unit variant", + "type": "string", + "enum": [ + "DocumentedUnit" + ] + }, + { + "type": "object", + "properties": { + "ValueNewType": {} + }, + "required": [ + "ValueNewType" + ], + "additionalProperties": false + } + ] + } } } } \ No newline at end of file