Skip to content

Commit

Permalink
panic in the face of type cycles (#151)
Browse files Browse the repository at this point in the history
* panic in the face of type cycles
These can arise because the schemars ignores conflicting names and
doesn't use e.g. the mod to qualify the name when necessary. There are
other errors that can result from this such as overwritten types, but
this is the most acute at the moment.
  • Loading branch information
ahl authored Oct 18, 2021
1 parent decd0ff commit 6703120
Showing 1 changed file with 57 additions and 2 deletions.
59 changes: 57 additions & 2 deletions dropshot/src/type_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* Utility functions for working with JsonSchema types.
*/

use std::collections::HashSet;

use indexmap::IndexMap;
use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec};

Expand Down Expand Up @@ -93,7 +95,7 @@ pub fn type_is_string_enum(
object: None,
reference: None,
extensions: _,
}) if matches!(instance_type.as_ref(), InstanceType::Array) => {
}) if instance_type.as_ref() == &InstanceType::Array => {
match array_validation.as_ref() {
schemars::schema::ArrayValidation {
items:
Expand All @@ -119,10 +121,11 @@ pub fn type_is_string_enum(
}
}

pub fn type_resolve<'a>(
fn type_resolve<'a>(
mut schema: &'a Schema,
dependencies: &'a IndexMap<String, Schema>,
) -> &'a Schema {
let mut set = HashSet::new();
while let Schema::Object(SchemaObject {
metadata: _,
instance_type: None,
Expand All @@ -138,6 +141,14 @@ pub fn type_resolve<'a>(
extensions: _,
}) = schema
{
if set.contains(ref_schema) {
eprintln!("{:#?}", schema);
eprintln!(
"consider #[serde(rename = \"...\")] or #[serde(transparent)]"
);
panic!("type reference cycle detected");
}
set.insert(ref_schema);
const PREFIX: &str = "#/components/schemas/";
assert!(ref_schema.starts_with(PREFIX));
schema = dependencies
Expand All @@ -146,3 +157,47 @@ pub fn type_resolve<'a>(
}
schema
}

#[cfg(test)]
mod tests {
use indexmap::IndexMap;
use schemars::schema::{Schema, SchemaObject};

use super::type_resolve;

#[test]
#[should_panic(expected = "type reference cycle detected")]
fn test_reflexive_type() {
let name = "#/components/schemas/Selfie".to_string();
let schema = Schema::Object(SchemaObject {
reference: Some(name),
..Default::default()
});

let mut dependencies = IndexMap::new();
dependencies.insert("Selfie".to_string(), schema.clone());
let schema_ref = &dependencies[0];

type_resolve(schema_ref, &dependencies);
}

#[test]
#[should_panic(expected = "type reference cycle detected")]
fn test_recursive_type() {
let jack_schema = Schema::Object(SchemaObject {
reference: Some("#/components/schemas/JohnJackson".to_string()),
..Default::default()
});
let john_schema = Schema::Object(SchemaObject {
reference: Some("#/components/schemas/JackJohnson".to_string()),
..Default::default()
});

let mut dependencies = IndexMap::new();
dependencies.insert("JackJohnson".to_string(), jack_schema);
dependencies.insert("JohnJackson".to_string(), john_schema);
let schema_ref = &dependencies[0];

type_resolve(schema_ref, &dependencies);
}
}

0 comments on commit 6703120

Please sign in to comment.