From 4ed1196c2a6b1250c4283e3cd07eb8ed8846ca42 Mon Sep 17 00:00:00 2001 From: QuartzLibrary <81446760+QuartzLibrary@users.noreply.github.com> Date: Sat, 1 Feb 2025 23:14:30 +0000 Subject: [PATCH] Simplify tests --- schema_analysis/tests/shared/mod.rs | 406 +++++++++++------- schema_analysis/tests/source_bson.rs | 96 +++-- schema_analysis/tests/source_cbor.rs | 88 ++-- schema_analysis/tests/source_json.rs | 86 ++-- schema_analysis/tests/source_toml.rs | 89 ++-- schema_analysis/tests/source_xml.rs | 213 +++++---- schema_analysis/tests/source_yaml.rs | 97 ++--- .../tests/target_json_schema_json_typegen.rs | 136 +++--- .../tests/target_json_schema_schemars.rs | 110 ++--- .../tests/target_json_typegen_shape.rs | 92 ++-- 10 files changed, 775 insertions(+), 638 deletions(-) diff --git a/schema_analysis/tests/shared/mod.rs b/schema_analysis/tests/shared/mod.rs index deb54a5..bbee9ce 100644 --- a/schema_analysis/tests/shared/mod.rs +++ b/schema_analysis/tests/shared/mod.rs @@ -1,19 +1,18 @@ -use std::collections::BTreeMap; - -use maplit::btreemap; - -use schema_analysis::{Field, FieldStatus, InferredSchema, Schema, StructuralEq}; +use schema_analysis::{InferredSchema, Schema, StructuralEq}; /// This provides a way for formats to quickly implement some basic tests. /// -/// Each format should implement the 'compare' function which normally will first serialize -/// a Rust representation of a value that should yield the required schema, then use the crate to -/// run the analysis on it and compare to a provided schema. -/// For each test the format might return `None` to skip it. -pub trait FormatTests { - fn convert_to_inferred_schema(value: T) -> InferredSchema; - fn compare(value: T, target_schema: Schema) { - let InferredSchema { schema } = Self::convert_to_inferred_schema(value); +/// Each format should implement the `infer_schema` function which will run the analysis. +/// +/// Tests may be skipped by reimplementing `test_*` functions to be no-ops. +pub trait FormatTests { + type Value; + + /// This is how the format runs inference on the value. + fn infer_schema(value: Self::Value) -> InferredSchema; + + fn compare(value: Self::Value, target_schema: Schema) { + let InferredSchema { schema } = Self::infer_schema(value); let success = schema.structural_eq(&target_schema); if !success { println!("INFERRED: {:#?}\n", schema); @@ -21,77 +20,224 @@ pub trait FormatTests { } assert!(success); } - fn _compare_option(value: Option, target_schema: Schema) { - if let Some(inner) = value { - Self::compare(inner, target_schema) - } - } - fn _compare_sequence(value: Option, field: Field) { - Self::_compare_option( - value, - Schema::Sequence { - field: Box::new(field), - context: Default::default(), - }, - ) - } - fn _compare_map_struct(value: Option, fields: BTreeMap) { - Self::_compare_option( - value, - Schema::Struct { - fields, - context: Default::default(), - }, - ) - } - fn null() -> Option; + fn null() -> Self::Value; fn test_null() { - Self::_compare_option(Self::null(), Schema::Null(Default::default())) + Self::compare(Self::null(), targets::null()) } - fn boolean() -> Option; + fn boolean() -> Self::Value; fn test_boolean() { - Self::_compare_option(Self::boolean(), Schema::Boolean(Default::default())) + Self::compare(Self::boolean(), targets::boolean()) } - fn integer() -> Option; + fn integer() -> Self::Value; fn test_integer() { - Self::_compare_option(Self::integer(), Schema::Integer(Default::default())) + Self::compare(Self::integer(), targets::integer()) } - fn float() -> Option; + fn float() -> Self::Value; fn test_float() { - Self::_compare_option(Self::float(), Schema::Float(Default::default())) + Self::compare(Self::float(), targets::float()) } - fn string() -> Option; + fn string() -> Self::Value; fn test_string() { - Self::_compare_option(Self::string(), Schema::String(Default::default())) + Self::compare(Self::string(), targets::string()) } - fn empty_sequence() -> Option; + fn empty_sequence() -> Self::Value; fn test_empty_sequence() { + Self::compare(Self::empty_sequence(), targets::empty_sequence()) + } + fn string_sequence() -> Self::Value; + fn test_string_sequence() { + Self::compare(Self::string_sequence(), targets::string_sequence()) + } + fn integer_sequence() -> Self::Value; + fn test_integer_sequence() { + Self::compare(Self::integer_sequence(), targets::integer_sequence()) + } + fn mixed_sequence() -> Self::Value; + fn test_mixed_sequence() { + Self::compare(Self::mixed_sequence(), targets::mixed_sequence()) + } + fn optional_mixed_sequence() -> Self::Value; + fn test_optional_mixed_sequence() { + Self::compare( + Self::optional_mixed_sequence(), + targets::optional_mixed_sequence(), + ) + } + + fn empty_map_struct() -> Self::Value; + fn test_empty_map_struct() { + Self::compare(Self::empty_map_struct(), targets::empty_map_struct()); + } + fn map_struct_single() -> Self::Value; + fn test_map_struct_single() { + Self::compare(Self::map_struct_single(), targets::map_struct_single()); + } + fn map_struct_double() -> Self::Value; + fn test_map_struct_double() { + Self::compare(Self::map_struct_double(), targets::map_struct_double()); + } + fn sequence_map_struct_mixed() -> Self::Value; + fn test_sequence_map_struct_mixed() { + Self::compare( + Self::sequence_map_struct_mixed(), + targets::sequence_map_struct_mixed(), + ); + } + fn sequence_map_struct_optional_or_missing() -> Self::Value; + fn test_sequence_map_struct_optional_or_missing() { + Self::compare( + Self::sequence_map_struct_optional_or_missing(), + targets::sequence_map_struct_optional_or_missing(), + ); + } + fn map_struct_mixed_sequence() -> Self::Value; + fn test_map_struct_mixed_sequence() { + Self::compare( + Self::map_struct_mixed_sequence(), + targets::map_struct_mixed_sequence(), + ); + } + fn map_struct_mixed_sequence_optional() -> Self::Value; + fn test_map_struct_mixed_sequence_optional() { + Self::compare( + Self::map_struct_mixed_sequence_optional(), + targets::map_struct_mixed_sequence_optional(), + ); + } +} + +#[macro_export] +macro_rules! test_format { + ($F:ty) => { + #[test] + fn null() { + <$F>::test_null(); + } + #[test] + fn boolean() { + <$F>::test_boolean(); + } + #[test] + fn integer() { + <$F>::test_integer(); + } + #[test] + fn float() { + <$F>::test_float(); + } + #[test] + fn string() { + <$F>::test_string(); + } + + #[test] + fn empty_sequence() { + <$F>::test_empty_sequence(); + } + #[test] + fn string_sequence() { + <$F>::test_string_sequence(); + } + #[test] + fn integer_sequence() { + <$F>::test_integer_sequence(); + } + #[test] + fn mixed_sequence() { + <$F>::test_mixed_sequence(); + } + #[test] + fn optional_mixed_sequence() { + <$F>::test_optional_mixed_sequence(); + } + + #[test] + fn empty_map_struct() { + <$F>::test_empty_map_struct(); + } + #[test] + fn map_struct_single() { + <$F>::test_map_struct_single(); + } + #[test] + fn map_struct_double() { + <$F>::test_map_struct_double(); + } + #[test] + fn sequence_map_struct_mixed() { + <$F>::test_sequence_map_struct_mixed(); + } + #[test] + fn sequence_map_struct_optional_or_missing() { + <$F>::test_sequence_map_struct_optional_or_missing(); + } + #[test] + fn map_struct_mixed_sequence() { + <$F>::test_map_struct_mixed_sequence(); + } + #[test] + fn map_struct_mixed_sequence_optional() { + <$F>::test_map_struct_mixed_sequence_optional(); + } + }; +} + +mod targets { + use std::collections::BTreeMap; + + use maplit::btreemap; + + use schema_analysis::{Field, FieldStatus, Schema}; + + pub fn null() -> Schema { + Schema::Null(Default::default()) + } + pub fn boolean() -> Schema { + Schema::Boolean(Default::default()) + } + pub fn integer() -> Schema { + Schema::Integer(Default::default()) + } + pub fn float() -> Schema { + Schema::Float(Default::default()) + } + pub fn string() -> Schema { + Schema::String(Default::default()) + } + + pub fn empty_sequence() -> Schema { let mut field = Field::default(); field.status.may_be_missing = true; - Self::_compare_sequence(Self::empty_sequence(), field) + Schema::Sequence { + field: Box::new(field), + context: Default::default(), + } } - fn string_sequence() -> Option; - fn test_string_sequence() { + pub fn string_sequence() -> Schema { let mut field = Field { status: FieldStatus::default(), schema: Some(Schema::String(Default::default())), }; field.status.may_be_normal = true; - Self::_compare_sequence(Self::string_sequence(), field); + Schema::Sequence { + field: Box::new(field), + context: Default::default(), + } } - fn integer_sequence() -> Option; - fn test_integer_sequence() { + pub fn integer_sequence() -> Schema { let mut field = Field { status: FieldStatus::default(), schema: Some(Schema::Integer(Default::default())), }; field.status.may_be_normal = true; - Self::_compare_sequence(Self::integer_sequence(), field); + Schema::Sequence { + field: Box::new(field), + context: Default::default(), + } } - fn mixed_sequence() -> Option; - fn test_mixed_sequence() { + pub fn mixed_sequence() -> Schema { let mut field = Field { status: FieldStatus::default(), schema: Some(Schema::Union { @@ -102,10 +248,12 @@ pub trait FormatTests { }), }; field.status.may_be_normal = true; - Self::_compare_sequence(Self::mixed_sequence(), field); + Schema::Sequence { + field: Box::new(field), + context: Default::default(), + } } - fn optional_mixed_sequence() -> Option; - fn test_optional_mixed_sequence() { + pub fn optional_mixed_sequence() -> Schema { let mut field = Field { status: FieldStatus::default(), schema: Some(Schema::Union { @@ -117,16 +265,20 @@ pub trait FormatTests { }; field.status.may_be_normal = true; field.status.may_be_null = true; - Self::_compare_sequence(Self::optional_mixed_sequence(), field); + Schema::Sequence { + field: Box::new(field), + context: Default::default(), + } } - fn empty_map_struct() -> Option; - fn test_empty_map_struct() { + pub fn empty_map_struct() -> Schema { let field_schemas: BTreeMap = BTreeMap::new(); - Self::_compare_map_struct(Self::empty_map_struct(), field_schemas); + Schema::Struct { + fields: field_schemas, + context: Default::default(), + } } - fn map_struct_single() -> Option; - fn test_map_struct_single() { + pub fn map_struct_single() -> Schema { let fields = { let mut hello_field = Field { status: FieldStatus::default(), @@ -137,10 +289,12 @@ pub trait FormatTests { "hello".into() => hello_field } }; - Self::_compare_map_struct(Self::map_struct_single(), fields); + Schema::Struct { + fields, + context: Default::default(), + } } - fn map_struct_double() -> Option; - fn test_map_struct_double() { + pub fn map_struct_double() -> Schema { let fields = { let mut hello_field = Field { status: FieldStatus::default(), @@ -157,10 +311,12 @@ pub trait FormatTests { "world".into() => world_field, } }; - Self::_compare_map_struct(Self::map_struct_double(), fields); + Schema::Struct { + fields, + context: Default::default(), + } } - fn sequence_map_struct_mixed() -> Option; - fn test_sequence_map_struct_mixed() { + pub fn sequence_map_struct_mixed() -> Schema { let inner_fields = { let mut hello_field = Field::with_schema(Schema::Integer(Default::default())); hello_field.status.may_be_normal = true; @@ -189,10 +345,12 @@ pub trait FormatTests { }); element_field.status.may_be_normal = true; - Self::_compare_sequence(Self::sequence_map_struct_mixed(), element_field); + Schema::Sequence { + field: Box::new(element_field), + context: Default::default(), + } } - fn sequence_map_struct_optional_or_missing() -> Option; - fn test_sequence_map_struct_optional_or_missing() { + pub fn sequence_map_struct_optional_or_missing() -> Schema { let inner_fields = { let mut hello_field = Field::with_schema(Schema::Integer(Default::default())); hello_field.status.may_be_normal = true; @@ -223,13 +381,12 @@ pub trait FormatTests { }); element_field.status.may_be_normal = true; - Self::_compare_sequence( - Self::sequence_map_struct_optional_or_missing(), - element_field, - ); + Schema::Sequence { + field: Box::new(element_field), + context: Default::default(), + } } - fn map_struct_mixed_sequence() -> Option; - fn test_map_struct_mixed_sequence() { + pub fn map_struct_mixed_sequence() -> Schema { let fields = { let mut hello_field = Field::with_schema(Schema::Integer(Default::default())); hello_field.status.may_be_normal = true; @@ -255,10 +412,12 @@ pub trait FormatTests { "sequence".into() => sequence_field, } }; - Self::_compare_map_struct(Self::map_struct_mixed_sequence(), fields); + Schema::Struct { + fields, + context: Default::default(), + } } - fn map_struct_mixed_sequence_optional() -> Option; - fn test_map_struct_mixed_sequence_optional() { + pub fn map_struct_mixed_sequence_optional() -> Schema { let fields = { let mut hello_field = Field::with_schema(Schema::Integer(Default::default())); hello_field.status.may_be_normal = true; @@ -289,82 +448,9 @@ pub trait FormatTests { "sequence".into() => sequence_field, } }; - Self::_compare_map_struct(Self::map_struct_mixed_sequence_optional(), fields); - } -} - -#[macro_export] -macro_rules! test_format { - ($F:ty) => { - #[test] - fn null() { - <$F>::test_null(); - } - #[test] - fn boolean() { - <$F>::test_boolean(); - } - #[test] - fn integer() { - <$F>::test_integer(); - } - #[test] - fn float() { - <$F>::test_float(); - } - #[test] - fn string() { - <$F>::test_string(); - } - - #[test] - fn empty_sequence() { - <$F>::test_empty_sequence(); - } - #[test] - fn string_sequence() { - <$F>::test_string_sequence(); - } - #[test] - fn integer_sequence() { - <$F>::test_integer_sequence(); - } - #[test] - fn mixed_sequence() { - <$F>::test_mixed_sequence(); - } - #[test] - fn optional_mixed_sequence() { - <$F>::test_optional_mixed_sequence(); - } - - #[test] - fn empty_map_struct() { - <$F>::test_empty_map_struct(); - } - #[test] - fn map_struct_single() { - <$F>::test_map_struct_single(); - } - #[test] - fn map_struct_double() { - <$F>::test_map_struct_double(); - } - #[test] - fn sequence_map_struct_mixed() { - <$F>::test_sequence_map_struct_mixed(); - } - #[test] - fn sequence_map_struct_optional_or_missing() { - <$F>::test_sequence_map_struct_optional_or_missing(); - } - #[test] - fn map_struct_mixed_sequence() { - <$F>::test_map_struct_mixed_sequence(); - } - #[test] - fn map_struct_mixed_sequence_optional() { - <$F>::test_map_struct_mixed_sequence_optional(); + Schema::Struct { + fields, + context: Default::default(), } - }; + } } diff --git a/schema_analysis/tests/source_bson.rs b/schema_analysis/tests/source_bson.rs index 1681163..028e0d3 100644 --- a/schema_analysis/tests/source_bson.rs +++ b/schema_analysis/tests/source_bson.rs @@ -1,4 +1,4 @@ -use bson::{bson, Bson as Value}; +use bson::bson; use schema_analysis::InferredSchema; @@ -9,82 +9,94 @@ struct Bson; test_format!(Bson); -impl FormatTests for Bson { - fn convert_to_inferred_schema(value: Value) -> InferredSchema { +impl FormatTests for Bson { + type Value = bson::Bson; + + fn infer_schema(value: Self::Value) -> InferredSchema { let bytes = bson::to_vec(&value).unwrap(); let processed_schema: InferredSchema = bson::from_slice(&bytes).unwrap(); processed_schema } // Bson doesn't allow top-level primitives - fn null() -> Option { - None + fn test_null() {} + fn null() -> Self::Value { + unreachable!() } - fn boolean() -> Option { - None + fn test_boolean() {} + fn boolean() -> Self::Value { + unreachable!() } - fn integer() -> Option { - None + fn test_integer() {} + fn integer() -> Self::Value { + unreachable!() } - fn float() -> Option { - None + fn test_float() {} + fn float() -> Self::Value { + unreachable!() } - fn string() -> Option { - None + fn test_string() {} + fn string() -> Self::Value { + unreachable!() } // Bson doesn't allow top-level arrays - fn empty_sequence() -> Option { - None + fn test_empty_sequence() {} + fn empty_sequence() -> Self::Value { + unreachable!() } - fn string_sequence() -> Option { - None + fn test_string_sequence() {} + fn string_sequence() -> Self::Value { + unreachable!() } - fn integer_sequence() -> Option { - None + fn test_integer_sequence() {} + fn integer_sequence() -> Self::Value { + unreachable!() } - fn mixed_sequence() -> Option { - None + fn test_mixed_sequence() {} + fn mixed_sequence() -> Self::Value { + unreachable!() } - fn optional_mixed_sequence() -> Option { - None + fn test_optional_mixed_sequence() {} + fn optional_mixed_sequence() -> Self::Value { + unreachable!() } - fn empty_map_struct() -> Option { - Some(bson!({})) + fn empty_map_struct() -> Self::Value { + bson!({}) } - fn map_struct_single() -> Option { - Some(bson!({ + fn map_struct_single() -> Self::Value { + bson!({ "hello": 1 - })) + }) } - fn map_struct_double() -> Option { - Some(bson!({ + fn map_struct_double() -> Self::Value { + bson!({ "hello": 1, "world": "!" - })) - } - fn sequence_map_struct_mixed() -> Option { - None // Bson doesn't allow top-level arrays + }) } fn test_sequence_map_struct_mixed() {} - fn sequence_map_struct_optional_or_missing() -> Option { - None // Bson doesn't allow top-level arrays + fn sequence_map_struct_mixed() -> Self::Value { + unreachable!() // Bson doesn't allow top-level arrays } fn test_sequence_map_struct_optional_or_missing() {} - fn map_struct_mixed_sequence() -> Option { - Some(bson!({ + fn sequence_map_struct_optional_or_missing() -> Self::Value { + unreachable!() // Bson doesn't allow top-level arrays + } + fn map_struct_mixed_sequence() -> Self::Value { + bson!({ "hello": 1, "world": "!", "sequence": ["one", "two", "three"] - })) + }) } - fn map_struct_mixed_sequence_optional() -> Option { - Some(bson!({ + fn map_struct_mixed_sequence_optional() -> Self::Value { + bson!({ "hello": 1, "world": "!", "optional": null, "sequence": ["one", "two", "three", null] - })) + }) } } diff --git a/schema_analysis/tests/source_cbor.rs b/schema_analysis/tests/source_cbor.rs index af639a7..9f02977 100644 --- a/schema_analysis/tests/source_cbor.rs +++ b/schema_analysis/tests/source_cbor.rs @@ -11,73 +11,71 @@ struct Cbor; test_format!(Cbor); -impl FormatTests for Cbor { - fn convert_to_inferred_schema(value: Value) -> InferredSchema { +impl FormatTests for Cbor { + type Value = Value; + + fn infer_schema(value: Value) -> InferredSchema { let vec_value = serde_cbor::to_vec(&value).unwrap(); let processed_schema: InferredSchema = serde_cbor::from_slice(&vec_value).unwrap(); processed_schema } - fn null() -> Option { - Some(Value::Null) + fn null() -> Self::Value { + Value::Null } - fn boolean() -> Option { - Some(Value::Bool(true)) + fn boolean() -> Self::Value { + Value::Bool(true) } - fn integer() -> Option { - Some(Value::Integer(123)) + fn integer() -> Self::Value { + Value::Integer(123) } - fn float() -> Option { - Some(Value::Float(123.123)) + fn float() -> Self::Value { + Value::Float(123.123) } - fn string() -> Option { - Some(Value::Text("hello".into())) + fn string() -> Self::Value { + Value::Text("hello".into()) } - fn empty_sequence() -> Option { - Some(Value::Array(vec![])) + fn empty_sequence() -> Self::Value { + Value::Array(vec![]) } - fn string_sequence() -> Option { - Some(Value::Array(vec![ + fn string_sequence() -> Self::Value { + Value::Array(vec![ Value::Text("one".into()), Value::Text("two".into()), Value::Text("three".into()), - ])) + ]) } - fn integer_sequence() -> Option { - Some(Value::Array(vec![1.into(), 2.into(), 3.into()])) + fn integer_sequence() -> Self::Value { + Value::Array(vec![1.into(), 2.into(), 3.into()]) } - fn mixed_sequence() -> Option { - Some(Value::Array(vec![ - 1.into(), - Value::Text("two".into()), - 3.into(), - ])) + fn mixed_sequence() -> Self::Value { + Value::Array(vec![1.into(), Value::Text("two".into()), 3.into()]) } - fn optional_mixed_sequence() -> Option { - Some(Value::Array(vec![ + fn optional_mixed_sequence() -> Self::Value { + Value::Array(vec![ 1.into(), Value::Text("two".into()), 3.into(), Value::Null, - ])) + ]) } - fn empty_map_struct() -> Option { - Some(Value::Map(BTreeMap::new())) + fn empty_map_struct() -> Self::Value { + Value::Map(BTreeMap::new()) } - fn map_struct_single() -> Option { + fn map_struct_single() -> Self::Value { let mut mapping = BTreeMap::new(); mapping.insert(Value::Text("hello".into()), 1.into()); - Some(Value::Map(mapping)) + Value::Map(mapping) } - fn map_struct_double() -> Option { + fn map_struct_double() -> Self::Value { let mut mapping = BTreeMap::new(); mapping.insert(Value::Text("hello".into()), 1.into()); mapping.insert(Value::Text("world".into()), Value::Text("!".into())); - Some(Value::Map(mapping)) + Value::Map(mapping) } - fn sequence_map_struct_mixed() -> Option { + fn sequence_map_struct_mixed() -> Self::Value { let mut mapping_1 = BTreeMap::new(); mapping_1.insert(Value::Text("hello".into()), 1.into()); mapping_1.insert(Value::Text("world".into()), Value::Text("!".into())); @@ -88,12 +86,9 @@ impl FormatTests for Cbor { mapping_2.insert(Value::Text("world".into()), Value::Text("!".into())); mapping_2.insert(Value::Text("mixed".into()), Value::Text("1.1".into())); - Some(Value::Array(vec![ - Value::Map(mapping_1), - Value::Map(mapping_2), - ])) + Value::Array(vec![Value::Map(mapping_1), Value::Map(mapping_2)]) } - fn sequence_map_struct_optional_or_missing() -> Option { + fn sequence_map_struct_optional_or_missing() -> Self::Value { let mut mapping_1 = BTreeMap::new(); mapping_1.insert(Value::Text("hello".into()), 1.into()); mapping_1.insert(Value::Text("possibly_null".into()), Value::Text("!".into())); @@ -104,12 +99,9 @@ impl FormatTests for Cbor { mapping_2.insert(Value::Text("hello".into()), 2.into()); mapping_2.insert(Value::Text("possibly_null".into()), Value::Null); - Some(Value::Array(vec![ - Value::Map(mapping_1), - Value::Map(mapping_2), - ])) + Value::Array(vec![Value::Map(mapping_1), Value::Map(mapping_2)]) } - fn map_struct_mixed_sequence() -> Option { + fn map_struct_mixed_sequence() -> Self::Value { let mut mapping = BTreeMap::new(); mapping.insert(Value::Text("hello".into()), 1.into()); mapping.insert(Value::Text("world".into()), Value::Text("!".into())); @@ -121,9 +113,9 @@ impl FormatTests for Cbor { Value::Text("three".into()), ]), ); - Some(Value::Map(mapping)) + Value::Map(mapping) } - fn map_struct_mixed_sequence_optional() -> Option { + fn map_struct_mixed_sequence_optional() -> Self::Value { let mut mapping = BTreeMap::new(); mapping.insert(Value::Text("hello".into()), 1.into()); mapping.insert(Value::Text("world".into()), Value::Text("!".into())); @@ -137,6 +129,6 @@ impl FormatTests for Cbor { Value::Null, ]), ); - Some(Value::Map(mapping)) + Value::Map(mapping) } } diff --git a/schema_analysis/tests/source_json.rs b/schema_analysis/tests/source_json.rs index f2de053..321f770 100644 --- a/schema_analysis/tests/source_json.rs +++ b/schema_analysis/tests/source_json.rs @@ -9,60 +9,62 @@ struct Json; test_format!(Json); -impl FormatTests for Json { - fn convert_to_inferred_schema(value: Value) -> InferredSchema { +impl FormatTests for Json { + type Value = Value; + + fn infer_schema(value: Self::Value) -> InferredSchema { let processed_schema: InferredSchema = serde_json::from_str(&value.to_string()).unwrap(); processed_schema } - fn null() -> Option { - Some(json!(null)) + fn null() -> Self::Value { + json!(null) } - fn boolean() -> Option { - Some(json!(true)) + fn boolean() -> Self::Value { + json!(true) } - fn integer() -> Option { - Some(json!(123)) + fn integer() -> Self::Value { + json!(123) } - fn float() -> Option { - Some(json!(123.123)) + fn float() -> Self::Value { + json!(123.123) } - fn string() -> Option { - Some(json!("hello there!")) + fn string() -> Self::Value { + json!("hello there!") } - fn empty_sequence() -> Option { - Some(json!([])) + fn empty_sequence() -> Self::Value { + json!([]) } - fn string_sequence() -> Option { - Some(json!(["one", "two", "three"])) + fn string_sequence() -> Self::Value { + json!(["one", "two", "three"]) } - fn integer_sequence() -> Option { - Some(json!([1, 2, 3])) + fn integer_sequence() -> Self::Value { + json!([1, 2, 3]) } - fn mixed_sequence() -> Option { - Some(json!([1, "two", 3])) + fn mixed_sequence() -> Self::Value { + json!([1, "two", 3]) } - fn optional_mixed_sequence() -> Option { - Some(json!([1, "two", 3, null])) + fn optional_mixed_sequence() -> Self::Value { + json!([1, "two", 3, null]) } - fn empty_map_struct() -> Option { - Some(json!({})) + fn empty_map_struct() -> Self::Value { + json!({}) } - fn map_struct_single() -> Option { - Some(json!({ + fn map_struct_single() -> Self::Value { + json!({ "hello": 1 - })) + }) } - fn map_struct_double() -> Option { - Some(json!({ + fn map_struct_double() -> Self::Value { + json!({ "hello": 1, "world": "!" - })) + }) } - fn sequence_map_struct_mixed() -> Option { - Some(json!([ + fn sequence_map_struct_mixed() -> Self::Value { + json!([ { "hello": 1, "world": "!", @@ -73,10 +75,10 @@ impl FormatTests for Json { "world": "!", "mixed": "1.1", } - ])) + ]) } - fn sequence_map_struct_optional_or_missing() -> Option { - Some(json!([ + fn sequence_map_struct_optional_or_missing() -> Self::Value { + json!([ { "hello": 1, "possibly_null": "!", @@ -87,21 +89,21 @@ impl FormatTests for Json { "hello": 2, "possibly_null": null, } - ])) + ]) } - fn map_struct_mixed_sequence() -> Option { - Some(json!({ + fn map_struct_mixed_sequence() -> Self::Value { + json!({ "hello": 1, "world": "!", "sequence": ["one", "two", "three"] - })) + }) } - fn map_struct_mixed_sequence_optional() -> Option { - Some(json!({ + fn map_struct_mixed_sequence_optional() -> Self::Value { + json!({ "hello": 1, "world": "!", "optional": null, "sequence": ["one", "two", "three", null] - })) + }) } } diff --git a/schema_analysis/tests/source_toml.rs b/schema_analysis/tests/source_toml.rs index 7390014..41dbd1d 100644 --- a/schema_analysis/tests/source_toml.rs +++ b/schema_analysis/tests/source_toml.rs @@ -9,69 +9,83 @@ struct Toml; test_format!(Toml); -impl FormatTests for Toml { - fn convert_to_inferred_schema(value: Value) -> InferredSchema { +impl FormatTests for Toml { + type Value = Value; + + fn infer_schema(value: Self::Value) -> InferredSchema { let string_value: String = toml::to_string(&value).unwrap(); let processed_schema: InferredSchema = toml::from_str(&string_value).unwrap(); processed_schema } - // Toml doesn't have null values - fn null() -> Option { - None + fn test_null() {} + fn null() -> Self::Value { + unreachable!() // Toml doesn't have null values } + // Toml doesn't allow top-level primitives - fn boolean() -> Option { - None + fn test_boolean() {} + fn boolean() -> Self::Value { + unreachable!() } - fn integer() -> Option { - None + fn test_integer() {} + fn integer() -> Self::Value { + unreachable!() } - fn float() -> Option { - None + fn test_float() {} + fn float() -> Self::Value { + unreachable!() } - fn string() -> Option { - None + fn test_string() {} + fn string() -> Self::Value { + unreachable!() } // Toml doesn't allow top-level arrays - fn empty_sequence() -> Option { - None + fn test_empty_sequence() {} + fn empty_sequence() -> Self::Value { + unreachable!() } - fn string_sequence() -> Option { - None + fn test_string_sequence() {} + fn string_sequence() -> Self::Value { + unreachable!() } - fn integer_sequence() -> Option { - None + fn test_integer_sequence() {} + fn integer_sequence() -> Self::Value { + unreachable!() } - fn mixed_sequence() -> Option { - None + fn test_mixed_sequence() {} + fn mixed_sequence() -> Self::Value { + unreachable!() } - fn optional_mixed_sequence() -> Option { - None + fn test_optional_mixed_sequence() {} + fn optional_mixed_sequence() -> Self::Value { + unreachable!() } - fn empty_map_struct() -> Option { - Some(Value::Table(Table::new())) + fn empty_map_struct() -> Self::Value { + Value::Table(Table::new()) } - fn map_struct_single() -> Option { + fn map_struct_single() -> Self::Value { let mut mapping = Table::new(); mapping.insert("hello".into(), 1.into()); - Some(Value::Table(mapping)) + Value::Table(mapping) } - fn map_struct_double() -> Option { + fn map_struct_double() -> Self::Value { let mut mapping = Table::new(); mapping.insert("hello".into(), 1.into()); mapping.insert("world".into(), "!".into()); - Some(Value::Table(mapping)) + Value::Table(mapping) } - fn sequence_map_struct_mixed() -> Option { - None // Toml doesn't allow top-level arrays + fn test_sequence_map_struct_mixed() {} + fn sequence_map_struct_mixed() -> Self::Value { + unreachable!() // Toml doesn't allow top-level arrays } - fn sequence_map_struct_optional_or_missing() -> Option { - None // Toml doesn't allow top-level arrays + fn test_sequence_map_struct_optional_or_missing() {} + fn sequence_map_struct_optional_or_missing() -> Self::Value { + unreachable!() // Toml doesn't allow top-level arrays } - fn map_struct_mixed_sequence() -> Option { + fn map_struct_mixed_sequence() -> Self::Value { let mut mapping = Table::new(); mapping.insert("hello".into(), 1.into()); mapping.insert("world".into(), "!".into()); @@ -79,9 +93,10 @@ impl FormatTests for Toml { "sequence".into(), Value::Array(vec!["one".into(), "two".into(), "three".into()]), ); - Some(Value::Table(mapping)) + Value::Table(mapping) } - fn map_struct_mixed_sequence_optional() -> Option { - None // Toml doesn't have null values + fn test_map_struct_mixed_sequence_optional() {} + fn map_struct_mixed_sequence_optional() -> Self::Value { + unreachable!() // Toml doesn't have null values } } diff --git a/schema_analysis/tests/source_xml.rs b/schema_analysis/tests/source_xml.rs index d208633..e92d758 100644 --- a/schema_analysis/tests/source_xml.rs +++ b/schema_analysis/tests/source_xml.rs @@ -1,8 +1,4 @@ -use std::collections::BTreeMap; - -use maplit::btreemap; - -use schema_analysis::{helpers, Field, FieldStatus, InferredSchema, Schema}; +use schema_analysis::{helpers, InferredSchema}; mod shared; use shared::FormatTests; @@ -11,57 +7,138 @@ struct Xml; test_format!(Xml); -impl FormatTests for Xml { - fn convert_to_inferred_schema(value: String) -> InferredSchema { +impl FormatTests for Xml { + type Value = String; + + fn infer_schema(value: Self::Value) -> InferredSchema { let mut processed_schema: InferredSchema = quick_xml::de::from_str(&value).unwrap(); helpers::xml::cleanup_xml_schema(&mut processed_schema.schema); processed_schema } // Xml doesn't allow top-level primitives - fn null() -> Option { - None + fn test_null() {} + fn null() -> Self::Value { + unreachable!() } - fn boolean() -> Option { - None + fn test_boolean() {} + fn boolean() -> Self::Value { + unreachable!() } - fn integer() -> Option { - None + fn test_integer() {} + fn integer() -> Self::Value { + unreachable!() } - fn float() -> Option { - None + fn test_float() {} + fn float() -> Self::Value { + unreachable!() } - fn string() -> Option { - None + fn test_string() {} + fn string() -> Self::Value { + unreachable!() } // Xml doesn't allow top-level arrays (quick_xml ignores later elements anyway) - fn empty_sequence() -> Option { - None + fn test_empty_sequence() {} + fn empty_sequence() -> Self::Value { + unreachable!() } - fn string_sequence() -> Option { - None + fn test_string_sequence() {} + fn string_sequence() -> Self::Value { + unreachable!() } - fn integer_sequence() -> Option { - None + fn test_integer_sequence() {} + fn integer_sequence() -> Self::Value { + unreachable!() } - fn mixed_sequence() -> Option { - None + fn test_mixed_sequence() {} + fn mixed_sequence() -> Self::Value { + unreachable!() } - fn optional_mixed_sequence() -> Option { - None + fn test_optional_mixed_sequence() {} + fn optional_mixed_sequence() -> Self::Value { + unreachable!() } // Note: root name is discarded - fn empty_map_struct() -> Option { - Some(r#""#.into()) + fn empty_map_struct() -> Self::Value { + r#""#.into() } - fn map_struct_single() -> Option { - Some(r#"1"#.into()) + fn map_struct_single() -> Self::Value { + r#"1"#.into() } fn test_map_struct_single() { - // Xml doesn't have integer values + Self::compare(Self::map_struct_single(), targets::map_struct_single()); + } + + fn map_struct_double() -> Self::Value { + r#"1!"#.into() + } + fn test_map_struct_double() { + Self::compare(Self::map_struct_double(), targets::map_struct_double()); + } + + // Xml only has strings, so there is no meaning to 'mixed'. + fn test_sequence_map_struct_mixed() {} + fn sequence_map_struct_mixed() -> Self::Value { + unreachable!() + } + + fn sequence_map_struct_optional_or_missing() -> Self::Value { + " + + + 1 + + 1.1 + + + + 1 + ! + + " + .into() + } + fn test_sequence_map_struct_optional_or_missing() { + Self::compare( + Self::sequence_map_struct_optional_or_missing(), + targets::sequence_map_struct_optional_or_missing(), + ); + } + + fn map_struct_mixed_sequence() -> Self::Value { + " + + 1 + ! + onetwothree + " + .into() + } + fn test_map_struct_mixed_sequence() { + Self::compare( + Self::map_struct_mixed_sequence(), + targets::map_struct_mixed_sequence(), + ); + } + // No built-in null makes this equivalent to the above. + fn test_map_struct_mixed_sequence_optional() {} + fn map_struct_mixed_sequence_optional() -> Self::Value { + unreachable!() + } +} + +/// We need to redefine some targets because, for example, xml doesn't have integer values. +mod targets { + use std::collections::BTreeMap; + + use maplit::btreemap; + + use schema_analysis::{Field, FieldStatus, Schema}; + + pub fn map_struct_single() -> Schema { let fields: BTreeMap = { let mut hello_field = Field { status: FieldStatus::default(), @@ -72,14 +149,12 @@ impl FormatTests for Xml { "hello".into() => hello_field } }; - Self::_compare_map_struct(Self::map_struct_single(), fields); - } - - fn map_struct_double() -> Option { - Some(r#"1!"#.into()) + Schema::Struct { + fields, + context: Default::default(), + } } - fn test_map_struct_double() { - // Xml doesn't have integer values + pub fn map_struct_double() -> Schema { let fields: BTreeMap = { let mut hello_field = Field { status: FieldStatus::default(), @@ -96,33 +171,13 @@ impl FormatTests for Xml { "world".into() => world_field, } }; - Self::_compare_map_struct(Self::map_struct_double(), fields); + Schema::Struct { + fields, + context: Default::default(), + } } - // Xml only has strings, so there is no meaning to 'mixed'. - fn sequence_map_struct_mixed() -> Option { - None - } - - fn sequence_map_struct_optional_or_missing() -> Option { - Some( - " - - - 1 - - 1.1 - - - - 1 - ! - - " - .into(), - ) - } - fn test_sequence_map_struct_optional_or_missing() { + pub fn sequence_map_struct_optional_or_missing() -> Schema { // NOTE: in xml sequences are detected as the same key appearing multiple times, so // the inner schema is correctly computed over all instances but it is not detected // as a sequence. @@ -168,26 +223,15 @@ impl FormatTests for Xml { }); sequence_field.status.may_be_normal = true; - Self::_compare_map_struct( - Self::sequence_map_struct_optional_or_missing(), - btreemap! { + Schema::Struct { + fields: btreemap! { "element".into() => sequence_field, }, - ); + context: Default::default(), + } } - fn map_struct_mixed_sequence() -> Option { - Some( - " - - 1 - ! - onetwothree - " - .into(), - ) - } - fn test_map_struct_mixed_sequence() { + pub fn map_struct_mixed_sequence() -> Schema { let fields: BTreeMap = { let mut hello_field = Field::with_schema(Schema::String(Default::default())); // hello_field.status.may_be_normal = true; @@ -214,10 +258,9 @@ impl FormatTests for Xml { "sequence".into() => sequence_field, } }; - Self::_compare_map_struct(Self::map_struct_mixed_sequence(), fields); - } - // No built-in null makes this equivalent to the above. - fn map_struct_mixed_sequence_optional() -> Option { - None + Schema::Struct { + fields, + context: Default::default(), + } } } diff --git a/schema_analysis/tests/source_yaml.rs b/schema_analysis/tests/source_yaml.rs index 4aa2526..2c6dddf 100644 --- a/schema_analysis/tests/source_yaml.rs +++ b/schema_analysis/tests/source_yaml.rs @@ -9,69 +9,62 @@ struct Yaml; test_format!(Yaml); -impl FormatTests for Yaml { - fn convert_to_inferred_schema(value: Value) -> InferredSchema { +impl FormatTests for Yaml { + type Value = Value; + + fn infer_schema(value: Self::Value) -> InferredSchema { let string_value: String = serde_yaml::to_string(&value).unwrap(); let processed_schema: InferredSchema = serde_yaml::from_str(&string_value).unwrap(); processed_schema } - fn null() -> Option { - Some(Value::Null) + fn null() -> Self::Value { + Value::Null } - fn boolean() -> Option { - Some(Value::Bool(true)) + fn boolean() -> Self::Value { + Value::Bool(true) } - fn integer() -> Option { - Some(Value::Number(123.into())) + fn integer() -> Self::Value { + Value::Number(123.into()) } - fn float() -> Option { - Some(Value::Number(123.123.into())) + fn float() -> Self::Value { + Value::Number(123.123.into()) } - fn string() -> Option { - Some(Value::String("hello".into())) + fn string() -> Self::Value { + Value::String("hello".into()) } - fn empty_sequence() -> Option { - Some(Value::Sequence(vec![])) - } - fn string_sequence() -> Option { - Some(Value::Sequence(vec![ - "one".into(), - "two".into(), - "three".into(), - ])) - } - fn integer_sequence() -> Option { - Some(Value::Sequence(vec![1.into(), 2.into(), 3.into()])) - } - fn mixed_sequence() -> Option { - Some(Value::Sequence(vec![1.into(), "two".into(), 3.into()])) - } - fn optional_mixed_sequence() -> Option { - Some(Value::Sequence(vec![ - 1.into(), - "two".into(), - 3.into(), - Value::Null, - ])) + fn empty_sequence() -> Self::Value { + Value::Sequence(vec![]) + } + fn string_sequence() -> Self::Value { + Value::Sequence(vec!["one".into(), "two".into(), "three".into()]) + } + fn integer_sequence() -> Self::Value { + Value::Sequence(vec![1.into(), 2.into(), 3.into()]) + } + fn mixed_sequence() -> Self::Value { + Value::Sequence(vec![1.into(), "two".into(), 3.into()]) + } + fn optional_mixed_sequence() -> Self::Value { + Value::Sequence(vec![1.into(), "two".into(), 3.into(), Value::Null]) } - fn empty_map_struct() -> Option { - Some(Value::Mapping(Mapping::new())) + fn empty_map_struct() -> Self::Value { + Value::Mapping(Mapping::new()) } - fn map_struct_single() -> Option { + fn map_struct_single() -> Self::Value { let mut mapping = Mapping::new(); mapping.insert("hello".into(), 1.into()); - Some(Value::Mapping(mapping)) + Value::Mapping(mapping) } - fn map_struct_double() -> Option { + fn map_struct_double() -> Self::Value { let mut mapping = Mapping::new(); mapping.insert("hello".into(), 1.into()); mapping.insert("world".into(), "!".into()); - Some(Value::Mapping(mapping)) + Value::Mapping(mapping) } - fn sequence_map_struct_mixed() -> Option { + fn sequence_map_struct_mixed() -> Self::Value { let mut mapping_1 = Mapping::new(); mapping_1.insert("hello".into(), 1.into()); mapping_1.insert("world".into(), "!".into()); @@ -82,12 +75,9 @@ impl FormatTests for Yaml { mapping_2.insert("world".into(), "!".into()); mapping_2.insert("mixed".into(), "1.1".into()); - Some(Value::Sequence(vec![ - Value::Mapping(mapping_1), - Value::Mapping(mapping_2), - ])) + Value::Sequence(vec![Value::Mapping(mapping_1), Value::Mapping(mapping_2)]) } - fn sequence_map_struct_optional_or_missing() -> Option { + fn sequence_map_struct_optional_or_missing() -> Self::Value { let mut mapping_1 = Mapping::new(); mapping_1.insert("hello".into(), 1.into()); mapping_1.insert("possibly_null".into(), "!".into()); @@ -98,12 +88,9 @@ impl FormatTests for Yaml { mapping_2.insert("hello".into(), 2.into()); mapping_2.insert("possibly_null".into(), Value::Null); - Some(Value::Sequence(vec![ - Value::Mapping(mapping_1), - Value::Mapping(mapping_2), - ])) + Value::Sequence(vec![Value::Mapping(mapping_1), Value::Mapping(mapping_2)]) } - fn map_struct_mixed_sequence() -> Option { + fn map_struct_mixed_sequence() -> Self::Value { let mut mapping = Mapping::new(); mapping.insert("hello".into(), 1.into()); mapping.insert("world".into(), "!".into()); @@ -111,9 +98,9 @@ impl FormatTests for Yaml { "sequence".into(), Value::Sequence(vec!["one".into(), "two".into(), "three".into()]), ); - Some(Value::Mapping(mapping)) + Value::Mapping(mapping) } - fn map_struct_mixed_sequence_optional() -> Option { + fn map_struct_mixed_sequence_optional() -> Self::Value { let mut mapping = Mapping::new(); mapping.insert("hello".into(), 1.into()); mapping.insert("world".into(), "!".into()); @@ -127,6 +114,6 @@ impl FormatTests for Yaml { Value::Null, ]), ); - Some(Value::Mapping(mapping)) + Value::Mapping(mapping) } } diff --git a/schema_analysis/tests/target_json_schema_json_typegen.rs b/schema_analysis/tests/target_json_schema_json_typegen.rs index b641619..b1f1d96 100644 --- a/schema_analysis/tests/target_json_schema_json_typegen.rs +++ b/schema_analysis/tests/target_json_schema_json_typegen.rs @@ -8,6 +8,8 @@ use schema_analysis::{InferredSchema, Schema}; mod shared; use shared::FormatTests; +const INTEGER: &str = "number"; // json_typegen always uses "number" + struct JSchema; test_format!(JSchema); @@ -15,8 +17,10 @@ test_format!(JSchema); const SCHEMA_TYPE: &str = "http://json-schema.org/draft-07/schema#"; const SCHEMA_TITLE: &str = "Generated schema for Root"; -impl FormatTests for JSchema { - fn convert_to_inferred_schema(_value: Value) -> InferredSchema { +impl FormatTests for JSchema { + type Value = Value; + + fn infer_schema(_value: Self::Value) -> InferredSchema { // Not needed for testing the target. unreachable!() } @@ -24,7 +28,7 @@ impl FormatTests for JSchema { // Note: here we are actually switching the source and target. // The target schema from the tests before is now being serialized to a json schema and then // parsed and compared to the json values below. - fn compare(target_json_schema: Value, tested_schema: Schema) { + fn compare(target_json_schema: Self::Value, tested_schema: Schema) { let serialized_json_schema = tested_schema .process_with_json_typegen(OutputMode::JsonSchema) .unwrap(); @@ -36,8 +40,8 @@ impl FormatTests for JSchema { ); } - fn null() -> Option { - Some(json! ({ + fn null() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, @@ -45,88 +49,86 @@ impl FormatTests for JSchema { // further information, but it's assumed an actual schema actually does exist // underneath, so we don't restrict the type to "null" here. // "type": "null", - })) + }) } - fn boolean() -> Option { - Some(json! ({ + fn boolean() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "boolean", - })) + }) } - fn integer() -> Option { - Some(json! ({ + fn integer() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, - // json_typegen always uses "number" - "type": "number", - })) + "type": INTEGER, + }) } - fn float() -> Option { - Some(json! ({ + fn float() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "number", - })) + }) } - fn string() -> Option { - Some(json! ({ + fn string() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "string", - })) + }) } - fn empty_sequence() -> Option { - Some(json!({ + fn empty_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "array", "items": {} - })) + }) } - fn string_sequence() -> Option { - Some(json!({ + fn string_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "array", "items": { "type": "string" } - })) + }) } - fn integer_sequence() -> Option { - Some(json!({ + fn integer_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "array", "items": { - // json_typegen always uses "number" - "type": "number" + "type": INTEGER } - })) + }) } - fn mixed_sequence() -> Option { - Some(json!({ + fn mixed_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "array", // json_typegen does not have a concept of union types. "items": {}, - })) + }) } - fn optional_mixed_sequence() -> Option { - Some(json!({ + fn optional_mixed_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "array", // json_typegen does not have a concept of union types. "items": {}, - })) + }) } - fn empty_map_struct() -> Option { - Some(json!({ + fn empty_map_struct() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "object", @@ -134,61 +136,57 @@ impl FormatTests for JSchema { // json_typegen always inserts these fields. "properties": {}, "required": [], - })) + }) } - fn map_struct_single() -> Option { - Some(json!({ + fn map_struct_single() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "object", "properties": { - // json_typegen always uses "number" - "hello": { "type": "number" } + "hello": { "type": INTEGER } }, "required": [ "hello" ] - })) + }) } - fn map_struct_double() -> Option { - Some(json!({ + fn map_struct_double() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "object", "properties": { - // json_typegen always uses "number" - "hello": { "type": "number" }, + "hello": { "type": INTEGER }, "world": { "type": "string" }, }, "required": [ "hello", "world" ] - })) + }) } - fn sequence_map_struct_mixed() -> Option { - Some(json!({ + fn sequence_map_struct_mixed() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "array", "items": { "type": "object", "properties": { - // json_typegen always uses "number" - "hello": { "type": "number" }, + "hello": { "type": INTEGER }, // json_typegen does not have a concept of union types. "mixed": {}, "world": { "type": "string" }, }, "required": [ "hello", "mixed", "world" ], } - })) + }) } - fn sequence_map_struct_optional_or_missing() -> Option { - Some(json!({ + fn sequence_map_struct_optional_or_missing() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "array", "items": { "type": "object", "properties": { - // json_typegen always uses "number" - "hello": { "type": "number" }, + "hello": { "type": INTEGER }, // We don't know what it is when it's not null, so it might be anything. "null_or_missing": {}, "possibly_missing": { "type": "number" }, @@ -201,16 +199,15 @@ impl FormatTests for JSchema { // for the "required" field. "required": [ "hello", "null_or_missing" ], } - })) + }) } - fn map_struct_mixed_sequence() -> Option { - Some(json!({ + fn map_struct_mixed_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "object", "properties": { - // json_typegen always uses "number" - "hello": { "type": "number" }, + "hello": { "type": INTEGER}, "sequence": { "type": "array", "items": { "type": "string" }, @@ -218,16 +215,15 @@ impl FormatTests for JSchema { "world": { "type": "string" }, }, "required": [ "hello", "sequence", "world" ], - })) + }) } - fn map_struct_mixed_sequence_optional() -> Option { - Some(json!({ + fn map_struct_mixed_sequence_optional() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "title": SCHEMA_TITLE, "type": "object", "properties": { - // json_typegen always uses "number" - "hello": { "type": "number" }, + "hello": { "type": INTEGER }, "optional": {}, "sequence": { "type": "array", @@ -241,6 +237,6 @@ impl FormatTests for JSchema { "world": { "type": "string" }, }, "required": [ "hello", "optional", "sequence", "world" ], - })) + }) } } diff --git a/schema_analysis/tests/target_json_schema_schemars.rs b/schema_analysis/tests/target_json_schema_schemars.rs index dc928c5..a09f25c 100644 --- a/schema_analysis/tests/target_json_schema_schemars.rs +++ b/schema_analysis/tests/target_json_schema_schemars.rs @@ -13,8 +13,10 @@ test_format!(JSchema); const SCHEMA_TYPE: &str = "https://json-schema.org/draft/2019-09/schema"; -impl FormatTests for JSchema { - fn convert_to_inferred_schema(_value: Value) -> InferredSchema { +impl FormatTests for JSchema { + type Value = Value; + + fn infer_schema(_value: Self::Value) -> InferredSchema { // Not needed for testing the target. unreachable!() } @@ -22,78 +24,78 @@ impl FormatTests for JSchema { // Note: here we are actually switching the source and target. // The target schema from the tests before is now being serialized to a json schema and then // parsed and compared to the json values below. - fn compare(target_json_schema: Value, tested_schema: Schema) { + fn compare(target_json_schema: Self::Value, tested_schema: Schema) { let serialized_json_schema = tested_schema.to_json_schema_with_schemars().unwrap(); let deserialized_json_schema: Value = serde_json::from_str(&serialized_json_schema).unwrap(); assert_eq!(deserialized_json_schema, target_json_schema); } - fn null() -> Option { - Some(json! ({ + fn null() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "type": "null", - })) + }) } - fn boolean() -> Option { - Some(json! ({ + fn boolean() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "type": "boolean", - })) + }) } - fn integer() -> Option { - Some(json! ({ + fn integer() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "type": "integer", - })) + }) } - fn float() -> Option { - Some(json! ({ + fn float() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "type": "number", - })) + }) } - fn string() -> Option { - Some(json! ({ + fn string() -> Self::Value { + json! ({ "$schema": SCHEMA_TYPE, "type": "string", - })) + }) } - fn empty_sequence() -> Option { - Some(json!({ + fn empty_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "array", "items": true - })) + }) } - fn string_sequence() -> Option { - Some(json!({ + fn string_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "array", "items": { "type": "string" } - })) + }) } - fn integer_sequence() -> Option { - Some(json!({ + fn integer_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "array", "items": { "type": "integer" } - })) + }) } - fn mixed_sequence() -> Option { - Some(json!({ + fn mixed_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "array", "items": { @@ -107,11 +109,11 @@ impl FormatTests for JSchema { }, ] } - })) + }) } - fn optional_mixed_sequence() -> Option { - Some(json!({ + fn optional_mixed_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "array", "items": { @@ -126,29 +128,29 @@ impl FormatTests for JSchema { { "type": "null" } ] } - })) + }) } - fn empty_map_struct() -> Option { - Some(json!({ + fn empty_map_struct() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "object", - })) + }) } - fn map_struct_single() -> Option { - Some(json!({ + fn map_struct_single() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "object", "properties": { "hello": { "type": "integer" } }, "required": [ "hello" ] - })) + }) } - fn map_struct_double() -> Option { - Some(json!({ + fn map_struct_double() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "object", "properties": { @@ -156,11 +158,11 @@ impl FormatTests for JSchema { "world": { "type": "string" }, }, "required": [ "hello", "world" ] - })) + }) } - fn sequence_map_struct_mixed() -> Option { - Some(json!({ + fn sequence_map_struct_mixed() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "array", "items": { @@ -177,11 +179,11 @@ impl FormatTests for JSchema { "world": { "type": "string" }, }, } - })) + }) } - fn sequence_map_struct_optional_or_missing() -> Option { - Some(json!({ + fn sequence_map_struct_optional_or_missing() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "array", "items": { @@ -195,11 +197,11 @@ impl FormatTests for JSchema { "possibly_null": { "type": ["string", "null"] } }, } - })) + }) } - fn map_struct_mixed_sequence() -> Option { - Some(json!({ + fn map_struct_mixed_sequence() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "object", "required": [ "hello", "sequence", "world" ], @@ -211,11 +213,11 @@ impl FormatTests for JSchema { "items": { "type": "string" }, }, }, - })) + }) } - fn map_struct_mixed_sequence_optional() -> Option { - Some(json!({ + fn map_struct_mixed_sequence_optional() -> Self::Value { + json!({ "$schema": SCHEMA_TYPE, "type": "object", "required": [ "hello", "optional", "sequence", "world" ], @@ -228,6 +230,6 @@ impl FormatTests for JSchema { "items": { "type": [ "string", "null" ] }, }, }, - })) + }) } } diff --git a/schema_analysis/tests/target_json_typegen_shape.rs b/schema_analysis/tests/target_json_typegen_shape.rs index aabccf6..f215284 100644 --- a/schema_analysis/tests/target_json_typegen_shape.rs +++ b/schema_analysis/tests/target_json_typegen_shape.rs @@ -12,8 +12,10 @@ struct JsonTypegen; test_format!(JsonTypegen); -impl FormatTests for JsonTypegen { - fn convert_to_inferred_schema(_shape: Shape) -> InferredSchema { +impl FormatTests for JsonTypegen { + type Value = Shape; + + fn infer_schema(_value: Self::Value) -> InferredSchema { // Not needed for testing the target. unreachable!() } @@ -21,76 +23,76 @@ impl FormatTests for JsonTypegen { // Note: here we are actually switching the source and target. // The target schema from the tests before is now being serialized to a json schema and then // parsed and compared to the json Shapes below. - fn compare(target_shape: Shape, tested_schema: Schema) { + fn compare(target_shape: Self::Value, tested_schema: Schema) { let tested_schema_shape: Shape = tested_schema.to_json_typegen_shape(); assert_eq!(tested_schema_shape, target_shape); } - fn null() -> Option { - Some(Shape::Null) + fn null() -> Self::Value { + Shape::Null } - fn boolean() -> Option { - Some(Shape::Bool) + fn boolean() -> Self::Value { + Shape::Bool } - fn integer() -> Option { - Some(Shape::Integer) + fn integer() -> Self::Value { + Shape::Integer } - fn float() -> Option { - Some(Shape::Floating) + fn float() -> Self::Value { + Shape::Floating } - fn string() -> Option { - Some(Shape::StringT) + fn string() -> Self::Value { + Shape::StringT } - fn empty_sequence() -> Option { - Some(Shape::VecT { + fn empty_sequence() -> Self::Value { + Shape::VecT { elem_type: Box::new(Shape::Bottom), - }) + } } - fn string_sequence() -> Option { - Some(Shape::VecT { + fn string_sequence() -> Self::Value { + Shape::VecT { elem_type: Box::new(Shape::StringT), - }) + } } - fn integer_sequence() -> Option { - Some(Shape::VecT { + fn integer_sequence() -> Self::Value { + Shape::VecT { elem_type: Box::new(Shape::Integer), - }) + } } - fn mixed_sequence() -> Option { - Some(Shape::VecT { + fn mixed_sequence() -> Self::Value { + Shape::VecT { elem_type: Box::new(Shape::Any), - }) + } } - fn optional_mixed_sequence() -> Option { - Some(Shape::VecT { + fn optional_mixed_sequence() -> Self::Value { + Shape::VecT { elem_type: Box::new(Shape::Optional(Box::new(Shape::Any))), - }) + } } - fn empty_map_struct() -> Option { - Some(Shape::Struct { + fn empty_map_struct() -> Self::Value { + Shape::Struct { fields: Default::default(), - }) + } } - fn map_struct_single() -> Option { + fn map_struct_single() -> Self::Value { // Note that the LinkedHashMap preserves order. let mut fields = LinkedHashMap::new(); fields.insert("hello".to_string(), Shape::Integer); - Some(Shape::Struct { fields }) + Shape::Struct { fields } } - fn map_struct_double() -> Option { + fn map_struct_double() -> Self::Value { // Note that the LinkedHashMap preserves order. let mut fields = LinkedHashMap::new(); fields.insert("hello".to_string(), Shape::Integer); fields.insert("world".to_string(), Shape::StringT); - Some(Shape::Struct { fields }) + Shape::Struct { fields } } - fn sequence_map_struct_mixed() -> Option { + fn sequence_map_struct_mixed() -> Self::Value { // Note that the LinkedHashMap preserves order. let mut fields = LinkedHashMap::new(); @@ -98,11 +100,11 @@ impl FormatTests for JsonTypegen { fields.insert("mixed".to_string(), Shape::Any); fields.insert("world".to_string(), Shape::StringT); - Some(Shape::VecT { + Shape::VecT { elem_type: Box::new(Shape::Struct { fields }), - }) + } } - fn sequence_map_struct_optional_or_missing() -> Option { + fn sequence_map_struct_optional_or_missing() -> Self::Value { // Note that the LinkedHashMap preserves order. let mut fields = LinkedHashMap::new(); @@ -117,11 +119,11 @@ impl FormatTests for JsonTypegen { Shape::Optional(Box::new(Shape::StringT)), ); - Some(Shape::VecT { + Shape::VecT { elem_type: Box::new(Shape::Struct { fields }), - }) + } } - fn map_struct_mixed_sequence() -> Option { + fn map_struct_mixed_sequence() -> Self::Value { // Note that the LinkedHashMap preserves order. let mut fields = LinkedHashMap::new(); @@ -134,9 +136,9 @@ impl FormatTests for JsonTypegen { ); fields.insert("world".to_string(), Shape::StringT); - Some(Shape::Struct { fields }) + Shape::Struct { fields } } - fn map_struct_mixed_sequence_optional() -> Option { + fn map_struct_mixed_sequence_optional() -> Self::Value { // Note that the LinkedHashMap preserves order. let mut fields = LinkedHashMap::new(); @@ -150,6 +152,6 @@ impl FormatTests for JsonTypegen { ); fields.insert("world".to_string(), Shape::StringT); - Some(Shape::Struct { fields }) + Shape::Struct { fields } } }