Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: GREsau/schemars
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.8.17
Choose a base ref
...
head repository: GREsau/schemars
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.8.19
Choose a head ref
  • 5 commits
  • 9 files changed
  • 2 contributors

Commits on May 6, 2024

  1. Simplify generated enum code (#286)

    * simplify the code generated for unit enums
    * simplify generated code for validating object properties
    * optimize internal and externally tagged enums
    
    ---------
    
    Co-authored-by: Robin Appelman <robin@icewind.nl>
    GREsau and icewind1991 authored May 6, 2024
    Copy the full SHA
    d04c17b View commit details
  2. v0.8.18

    GREsau committed May 6, 2024
    Copy the full SHA
    4b90b6f View commit details
  3. Copy the full SHA
    1a40d1b View commit details
  4. v0.8.19

    GREsau committed May 6, 2024
    Copy the full SHA
    8da70a4 View commit details
  5. Typo in changelog

    GREsau committed May 6, 2024
    Copy the full SHA
    7f6a7b7 View commit details
Showing with 182 additions and 128 deletions.
  1. +12 −0 CHANGELOG.md
  2. +2 −2 Cargo.lock
  3. +2 −2 schemars/Cargo.toml
  4. +117 −13 schemars/src/_private.rs
  5. +8 −0 schemars/tests/enum.rs
  6. +6 −0 schemars/tests/expected/no-variants.json
  7. +1 −1 schemars_derive/Cargo.toml
  8. +22 −38 schemars_derive/src/metadata.rs
  9. +12 −72 schemars_derive/src/schema_exprs.rs
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## [0.8.19] - 2024-05-06

### Fixed:

- Regression that caused a compile error when deriving `JsonSchema` on an enum with no variants (https://github.com/GREsau/schemars/issues/287)

## [0.8.18] - 2024-05-06

### Fixed:

- Reduce size of MIR output (and improve release-mode compile time) when deriving `JsonSchema` on enums (https://github.com/GREsau/schemars/pull/266 / https://github.com/GREsau/schemars/pull/286)

## [0.8.17] - 2024-04-28

### Changed:
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions schemars/Cargo.toml
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ name = "schemars"
description = "Generate JSON Schemas from Rust code"
homepage = "https://graham.cool/schemars/"
repository = "https://github.com/GREsau/schemars"
version = "0.8.17"
version = "0.8.19"
authors = ["Graham Esau <gesau@hotmail.co.uk>"]
edition = "2021"
license = "MIT"
@@ -14,7 +14,7 @@ build = "build.rs"
rust-version = "1.60"

[dependencies]
schemars_derive = { version = "=0.8.17", optional = true, path = "../schemars_derive" }
schemars_derive = { version = "=0.8.19", optional = true, path = "../schemars_derive" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.25"
dyn-clone = "1.0"
130 changes: 117 additions & 13 deletions schemars/src/_private.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::flatten::Merge;
use crate::gen::SchemaGenerator;
use crate::schema::{Metadata, Schema, SchemaObject};
use crate::JsonSchema;
use crate::schema::{InstanceType, ObjectValidation, Schema, SchemaObject};
use crate::{JsonSchema, Map, Set};
use serde::Serialize;
use serde_json::Value;

@@ -25,16 +24,6 @@ pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
schema
}

pub fn apply_metadata(schema: Schema, metadata: Metadata) -> Schema {
if metadata == Metadata::default() {
schema
} else {
let mut schema_obj = schema.into_object();
schema_obj.metadata = Some(Box::new(metadata)).merge(schema_obj.metadata);
Schema::Object(schema_obj)
}
}

/// Hack to simulate specialization:
/// `MaybeSerializeWrapper(x).maybe_to_value()` will resolve to either
/// - The inherent method `MaybeSerializeWrapper::maybe_to_value(...)` if x is `Serialize`
@@ -65,3 +54,118 @@ impl<T: Serialize> MaybeSerializeWrapper<T> {
serde_json::value::to_value(self.0).ok()
}
}

/// Create a schema for a unit enum
pub fn new_unit_enum(variant: &str) -> Schema {
Schema::Object(SchemaObject {
instance_type: Some(InstanceType::String.into()),
enum_values: Some(vec![variant.into()]),
..SchemaObject::default()
})
}

/// Create a schema for an externally tagged enum
pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema {
Schema::Object(SchemaObject {
instance_type: Some(InstanceType::Object.into()),
object: Some(Box::new(ObjectValidation {
properties: {
let mut props = Map::new();
props.insert(variant.to_owned(), sub_schema);
props
},
required: {
let mut required = Set::new();
required.insert(variant.to_owned());
required
},
// Externally tagged variants must prohibit additional
// properties irrespective of the disposition of
// `deny_unknown_fields`. If additional properties were allowed
// one could easily construct an object that validated against
// multiple variants since here it's the properties rather than
// the values of a property that distingish between variants.
additional_properties: Some(Box::new(false.into())),
..Default::default()
})),
..SchemaObject::default()
})
}

/// Create a schema for an internally tagged enum
pub fn new_internally_tagged_enum(
tag_name: &str,
variant: &str,
deny_unknown_fields: bool,
) -> Schema {
let tag_schema = Schema::Object(SchemaObject {
instance_type: Some(InstanceType::String.into()),
enum_values: Some(vec![variant.into()]),
..Default::default()
});
Schema::Object(SchemaObject {
instance_type: Some(InstanceType::Object.into()),
object: Some(Box::new(ObjectValidation {
properties: {
let mut props = Map::new();
props.insert(tag_name.to_owned(), tag_schema);
props
},
required: {
let mut required = Set::new();
required.insert(tag_name.to_owned());
required
},
additional_properties: deny_unknown_fields.then(|| Box::new(false.into())),
..Default::default()
})),
..SchemaObject::default()
})
}

pub fn insert_object_property<T: ?Sized + JsonSchema>(
obj: &mut ObjectValidation,
key: &str,
has_default: bool,
required: bool,
schema: Schema,
) {
obj.properties.insert(key.to_owned(), schema);
if required || !(has_default || T::_schemars_private_is_option()) {
obj.required.insert(key.to_owned());
}
}

pub mod metadata {
use crate::Schema;
use serde_json::Value;

macro_rules! add_metadata_fn {
($method:ident, $name:ident, $ty:ty) => {
pub fn $method(schema: Schema, $name: impl Into<$ty>) -> Schema {
let value = $name.into();
if value == <$ty>::default() {
schema
} else {
let mut schema_obj = schema.into_object();
schema_obj.metadata().$name = value.into();
Schema::Object(schema_obj)
}
}
};
}

add_metadata_fn!(add_description, description, String);
add_metadata_fn!(add_id, id, String);
add_metadata_fn!(add_title, title, String);
add_metadata_fn!(add_deprecated, deprecated, bool);
add_metadata_fn!(add_read_only, read_only, bool);
add_metadata_fn!(add_write_only, write_only, bool);
add_metadata_fn!(add_default, default, Value);

pub fn add_examples<I: IntoIterator<Item = Value>>(schema: Schema, examples: I) -> Schema {
let mut schema_obj = schema.into_object();
schema_obj.metadata().examples.extend(examples);
Schema::Object(schema_obj)
}
}
8 changes: 8 additions & 0 deletions schemars/tests/enum.rs
Original file line number Diff line number Diff line change
@@ -136,3 +136,11 @@ enum SoundOfMusic {
fn enum_unit_with_doc_comments() -> TestResult {
test_default_generated_schema::<SoundOfMusic>("enum-unit-doc")
}

#[derive(JsonSchema)]
enum NoVariants {}

#[test]
fn enum_no_variants() -> TestResult {
test_default_generated_schema::<NoVariants>("no-variants")
}
6 changes: 6 additions & 0 deletions schemars/tests/expected/no-variants.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "NoVariants",
"type": "string",
"enum": []
}
2 changes: 1 addition & 1 deletion schemars_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ name = "schemars_derive"
description = "Macros for #[derive(JsonSchema)], for use with schemars"
homepage = "https://graham.cool/schemars/"
repository = "https://github.com/GREsau/schemars"
version = "0.8.17"
version = "0.8.19"
authors = ["Graham Esau <gesau@hotmail.co.uk>"]
edition = "2021"
license = "MIT"
60 changes: 22 additions & 38 deletions schemars_derive/src/metadata.rs
Original file line number Diff line number Diff line change
@@ -13,47 +13,32 @@ pub struct SchemaMetadata<'a> {

impl<'a> SchemaMetadata<'a> {
pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) {
let setters = self.make_setters();
if !setters.is_empty() {
*schema_expr = quote! {{
let schema = #schema_expr;
schemars::_private::apply_metadata(schema, schemars::schema::Metadata {
#(#setters)*
..Default::default()
})
}}
}
}

fn make_setters(&self) -> Vec<TokenStream> {
let mut setters = Vec::<TokenStream>::new();

if let Some(title) = &self.title {
setters.push(quote! {
title: Some(#title.to_owned()),
});
*schema_expr = quote! {
schemars::_private::metadata::add_title(#schema_expr, #title)
};
}
if let Some(description) = &self.description {
setters.push(quote! {
description: Some(#description.to_owned()),
});
*schema_expr = quote! {
schemars::_private::metadata::add_description(#schema_expr, #description)
};
}

if self.deprecated {
setters.push(quote! {
deprecated: true,
});
*schema_expr = quote! {
schemars::_private::metadata::add_deprecated(#schema_expr, true)
};
}

if self.read_only {
setters.push(quote! {
read_only: true,
});
*schema_expr = quote! {
schemars::_private::metadata::add_read_only(#schema_expr, true)
};
}
if self.write_only {
setters.push(quote! {
write_only: true,
});
*schema_expr = quote! {
schemars::_private::metadata::add_write_only(#schema_expr, true)
};
}

if !self.examples.is_empty() {
@@ -62,17 +47,16 @@ impl<'a> SchemaMetadata<'a> {
schemars::_serde_json::value::to_value(#eg())
}
});
setters.push(quote! {
examples: vec![#(#examples),*].into_iter().flatten().collect(),
});

*schema_expr = quote! {
schemars::_private::metadata::add_examples(#schema_expr, [#(#examples),*].into_iter().flatten())
};
}

if let Some(default) = &self.default {
setters.push(quote! {
default: #default.and_then(|d| schemars::_schemars_maybe_to_value!(d)),
});
*schema_expr = quote! {
schemars::_private::metadata::add_default(#schema_expr, #default.and_then(|d| schemars::_schemars_maybe_to_value!(d)))
};
}

setters
}
}
Loading