Skip to content

Commit

Permalink
Use $defs instead of definitions in draft 2019-09
Browse files Browse the repository at this point in the history
This also changes the strategy for running visitors on subschemas - it is now done eagerly, as soon as they're added to the definitions map.
  • Loading branch information
GREsau committed May 19, 2024
1 parent 22e89a5 commit 95475ad
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 74 deletions.
106 changes: 64 additions & 42 deletions schemars/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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::<T>(id);
let mut schema = self.json_schema_internal::<T>(id);
Self::run_visitors(&mut schema, &mut self.settings.visitors);

self.definitions.insert(name, schema.to_value());
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -450,6 +427,51 @@ impl SchemaGenerator {
let pss = PendingSchemaState::new(self, id);
T::json_schema(pss.gen)
}

fn add_definitions(
schema_object: &mut Map<String, Value>,
mut definitions: Map<String, Value>,
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<String, Value>,
pointer: &str,
) -> Option<&'a mut Map<String, Value>> {
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<dyn GenVisitor>]) {
for visitor in visitors {
visitor.visit_schema(schema);
}
}
}

/// A [Visitor](Visitor) which implements additional traits required to be included in a [SchemaSettings].
Expand Down
2 changes: 1 addition & 1 deletion schemars/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub fn visit_schema<V: Visitor + ?Sized>(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() {
Expand Down
4 changes: 2 additions & 2 deletions schemars/tests/expected/doc_comments_struct_ref_siblings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
},
"my_unit": {
"description": "A unit struct instance",
"$ref": "#/definitions/MyUnitStruct"
"$ref": "#/$defs/MyUnitStruct"
}
},
"required": [
"my_int",
"my_undocumented_bool",
"my_unit"
],
"definitions": {
"$defs": {
"MyUnitStruct": {
"title": "A Unit",
"type": "null"
Expand Down
4 changes: 2 additions & 2 deletions schemars/tests/expected/schema_settings-2019_09.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"inner": {
"anyOf": [
{
"$ref": "#/definitions/Inner"
"$ref": "#/$defs/Inner"
},
{
"type": "null"
Expand All @@ -32,7 +32,7 @@
"values",
"value"
],
"definitions": {
"$defs": {
"Inner": {
"oneOf": [
{
Expand Down
56 changes: 29 additions & 27 deletions schemars/tests/expected/schema_settings-openapi3.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
}
}
}
}

0 comments on commit 95475ad

Please sign in to comment.