Skip to content

Commit

Permalink
Add SchemaGenerator to transforms
Browse files Browse the repository at this point in the history
  • Loading branch information
david.bonet committed Jan 30, 2025
1 parent efb7496 commit 708f9ad
Show file tree
Hide file tree
Showing 16 changed files with 188 additions and 138 deletions.
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 definitions 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

0 comments on commit 708f9ad

Please sign in to comment.