Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SchemaGenerator to transforms #370

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions docs/0-migrating.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,45 +126,47 @@ use schemars::Schema;
pub struct MyTransform;

impl Transform for MyTransform {
fn transform(&mut self, schema: &mut Schema) {
fn transform(&mut self, schema: &mut Schema, generator: &mut SchemaGenerator) {
// First, make our change to this schema
schema.insert("my_property".to_string(), serde_json::json!("hello world"));

// Then apply the transform to any subschemas
transform_subschemas(self, schema);
transform_subschemas(self, schema, generator);
}
}

let mut schema = schemars::schema_for!(str);
MyTransform.transform(&mut schema);
```

Also, since `Transform` is now implemented for functions that take a single `&mut Schema` argument, you could also define it as a function instead of a struct:
Also, since `Transform` is now implemented for functions that take a `&mut Schema` and `&mut SchemaGenerator` arguments, you could also define it as a function instead of a struct:

```rust
fn my_transform(schema: &mut Schema) {
fn my_transform(schema: &mut Schema, generator: &mut SchemaGenerator) {
// First, make our change to this schema
schema.insert("my_property".to_string(), serde_json::json!("hello world"));

// Then apply the transform to any subschemas
transform_subschemas(&mut my_transform, schema);
transform_subschemas(&mut my_transform, schema, generator);
}

let mut schema = schemars::schema_for!(str);
my_transform(&mut schema);
let mut generator = SchemaGenerator::default();
my_transform(&mut schema, &mut generator);
// Or equivalently:
// my_transform.transform(&mut schema);
// my_transform.transform(&mut schema, &mut generator);
```

Finally, you can also use the `RecursiveTransform` newtype to convert a non-recursive `Transform` (i.e. one that does not transform subschemas) into a recursive one, like so:

```rust
fn my_transform2(schema: &mut Schema) {
fn my_transform2(schema: &mut Schema, _generator: &mut SchemaGenerator) {
schema.insert("my_property".to_string(), serde_json::json!("hello world"));
}

let mut schema = schemars::schema_for!(str);
RecursiveTransform(my_transform2).transform(&mut schema);
let mut generator = SchemaGenerator::default();
RecursiveTransform(my_transform2).transform(&mut schema, &mut generator);
```

## Changes to `#[validate(...)]` attributes
Expand Down
6 changes: 4 additions & 2 deletions docs/_includes/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,10 +348,10 @@ struct Struct;

Set on a container, variant or field to run a `schemars::transform::Transform` against the generated schema. This can be specified multiple times to run multiple transforms.

The `Transform` trait is implemented on functions with the signature `fn(&mut Schema) -> ()`, allowing you to do this:
The `Transform` trait is implemented on functions with the signature `fn(&mut Schema, &mut SchemaGenerator) -> ()`, allowing you to do this:

```rust
fn my_transform(schema: &mut Schema) {
fn my_transform(schema: &mut Schema, generator: &mut SchemaGenerator) {
todo!()
}

Expand All @@ -360,6 +360,8 @@ fn my_transform(schema: &mut Schema) {
struct Struct;
```

The transform function can use the `SchemaGenerator` to inject schemas in the `$defs` section.

<h3 id="doc">

Doc Comments (`#[doc = "..."]`)
Expand Down
2 changes: 1 addition & 1 deletion docs/_includes/examples/schemars_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub enum MyEnum {
},
}

fn remove_format(schema: &mut Schema) {
fn remove_format(schema: &mut Schema, _generator: &mut SchemaGenerator) {
schema.remove("format");
}

Expand Down
4 changes: 2 additions & 2 deletions schemars/examples/schemars_attrs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use schemars::{schema_for, JsonSchema, Schema};
use schemars::{schema_for, JsonSchema, Schema, SchemaGenerator};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, JsonSchema)]
Expand All @@ -24,7 +24,7 @@ pub enum MyEnum {
},
}

fn remove_format(schema: &mut Schema) {
fn remove_format(schema: &mut Schema, _generator: &mut SchemaGenerator) {
schema.remove("format");
}

Expand Down
8 changes: 4 additions & 4 deletions schemars/src/_private/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub fn json_schema_for_internally_tagged_enum_newtype_variant<T: ?Sized + JsonSc
// but only on immediate subschemas.

let mut transform = AllowUnknownProperties::default();
transform_immediate_subschemas(&mut transform, &mut schema);
transform_immediate_subschemas(&mut transform, &mut schema, generator);

if T::always_inline_schema()
|| generator.settings().inline_subschemas
Expand Down Expand Up @@ -58,7 +58,7 @@ pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(

// Always allow aditional/unevaluated properties, because the outer struct determines
// whether it denies unknown fields.
AllowUnknownProperties::default().transform(&mut schema);
AllowUnknownProperties::default().transform(&mut schema, generator);

schema
}
Expand All @@ -69,7 +69,7 @@ struct AllowUnknownProperties {
}

impl Transform for AllowUnknownProperties {
fn transform(&mut self, schema: &mut Schema) {
fn transform(&mut self, schema: &mut Schema, generator: &mut SchemaGenerator) {
if schema.get("additionalProperties").and_then(Value::as_bool) == Some(false) {
schema.remove("additionalProperties");
self.did_modify = true;
Expand All @@ -79,7 +79,7 @@ impl Transform for AllowUnknownProperties {
self.did_modify = true;
}

transform_immediate_subschemas(self, schema);
transform_immediate_subschemas(self, schema, generator);
}
}

Expand Down
23 changes: 20 additions & 3 deletions schemars/src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,21 @@ impl SchemaGenerator {
self.settings.transforms.iter_mut().map(Box::as_mut)
}

/// Returns an iterator over the [transforms](SchemaSettings::transforms) being used by this
/// `SchemaGenerator`.
fn take_transforms(&mut self) -> Vec<Box<dyn GenTransform>> {
core::mem::take(&mut self.settings.transforms)
}

/// Returns an iterator over the [transforms](SchemaSettings::transforms) being used by this
/// `SchemaGenerator`.
fn set_transforms(
&mut self,
transforms: Vec<Box<dyn GenTransform>>,
) -> Vec<Box<dyn GenTransform>> {
core::mem::replace(&mut self.settings.transforms, transforms)
}

/// Generates a JSON Schema for the type `T`.
///
/// If `T`'s schema depends on any [non-inlined](JsonSchema::always_inline_schema) schemas, then
Expand Down Expand Up @@ -538,9 +553,11 @@ impl SchemaGenerator {
}

fn apply_transforms(&mut self, schema: &mut Schema) {
for transform in self.transforms_mut() {
transform.transform(schema);
let mut transforms = self.take_transforms();
for transform in &mut transforms {
transform.transform(schema, self);
}
self.set_transforms(transforms);
}

/// Returns `self.settings.definitions_path` as a plain JSON pointer to the definitions object,
Expand Down Expand Up @@ -606,7 +623,7 @@ fn json_pointer_mut<'a>(
/// struct MyTransform;
///
/// impl Transform for MyTransform {
/// fn transform(&mut self, schema: &mut schemars::Schema) {
/// fn transform(&mut self, schema: &mut schemars::Schema, generator: &mut schemars::SchemaGenerator) {
/// todo!()
/// }
/// }
Expand Down
Loading