Skip to content

(GH-538) Define transforms for Option<T> - #1702

Merged
Steve Lee (SteveL-MSFT) merged 3 commits into
PowerShell:mainfrom
michaeltlombardi:gh-538/main/idiomaticize-options
Sep 3, 2026
Merged

(GH-538) Define transforms for Option<T>#1702
Steve Lee (SteveL-MSFT) merged 3 commits into
PowerShell:mainfrom
michaeltlombardi:gh-538/main/idiomaticize-options

Conversation

@michaeltlombardi

Copy link
Copy Markdown
Collaborator

PR Summary

This change:

  • Adds the idiomaticize_option_field transform to address this limitation of schemars for a single field or variant.

  • Adds the idiomaticize_optional_properties transform to address this limitation of schemars at the top level of a struct, applying the idiomaticize_option_field transform to every optional field in the struct.

  • Adds new helpers to SchemaUtilityExtensions for ease of use in the implementation of the new transforms:

    • get_defined_keywords to return every keyword defined at the top level of the schema.
    • get_properties_keys to return the keys of the properties object at the top level of the schema.
    • get_required_property_names to return the names of the properties defined in the required keyword at the top level of the schema.
  • Includes documentation and integration testing

PR Context

Prior to this change we had no (relatively) convenient way to make the emitted schemas for structs with Option<T> fields more idiomatic.

As of the v1.0.0 release of schemars, the schema generator always wraps handling for Option<T> fields. The emitted schema depends on T:

  • When T is for a primitive type, like String or bool, the emitted schema looks like:

      {
        "type": "object",
        "properties": {
            "string_field": {
                "type": ["string", "null"]
            },
            "boolean_field": {
                "type": ["boolean", "null"]
            }
        }
    }
  • When T is for an inlined string enum, the emitted schema looks like:

    {
        "type": "object",
        "properties": {
            "enum_field": {
                "type": ["string", "null"],
                "enum": ["Variant1", "Variant2", "Variant3", null]
            }
        }
    }
  • When T is for an inlined struct, the emitted schema looks like:

    {
        "type": "object",
        "properties": {
            "struct_field": {
                "type": ["string", "null"],
                "pattern": "^[a-zA-Z0-9_]+$"
            }
        }
    }
  • When T is for an enum or struct that isn't inlined, the emitted schema looks like:

    {
        "type": "object",
        "properties": {
            "non_inlined_field": {
                "anyOf": [
                    { "$ref": "#/$defs/NonInlinedType" },
                    { "type": "null" }
                ]
            }
        },
        "$defs": {
            "NonInlinedType": {
                "type": "object",
                "properties": {
                    "inner_field": {
                        "type": "string"
                    }
                }
            }
        }
    }

All of these representations are non-idiomatic. In JSON Schema, explicitly defining a field as null is not equivalent to not specifying the field. We should only permit specifying a field as null when this is semantically accurate, not as shorthand for "not defined."

We control whether a property is mandatory in the schema with other keywords, like required and dependentRequired.

Prior to this change we had no (relatively) convenient way to make the
emitted schemas for structs with `Option<T>` fields more idiomatic.

As of the `v1.0.0` release of schemars, the schema generator _always_
wraps handling for `Option<T>` fields. The emitted schema depends on
`T`:

- When `T` is for a primitive type, like `String` or `bool`, the emitted
  schema looks like:

  ```json
  {
    "type": "object",
    "properties": {
        "string_field": {
            "type": ["string", "null"]
        },
        "boolean_field": {
            "type": ["boolean", "null"]
        }
    }
  }
  ```

- When `T` is for an inlined string enum, the emitted schema looks like:

  ```json
  {
    "type": "object",
    "properties": {
        "enum_field": {
            "type": ["string", "null"],
            "enum": ["Variant1", "Variant2", "Variant3", null]
        }
    }
  }
  ```

- When `T` is for an inlined struct, the emitted schema looks like:

  ```json
  {
    "type": "object",
    "properties": {
        "struct_field": {
            "type": ["string", "null"],
            "pattern": "^[a-zA-Z0-9_]+$"
        }
    }
  }
  ```
- When `T` is for an enum or struct that isn't inlined, the emitted
  schema looks like:

  ```json
  {
    "type": "object",
    "properties": {
        "non_inlined_field": {
            "anyOf": [
                { "$ref": "#/$defs/NonInlinedType" },
                { "type": "null" }
            ]
        }
    },
    "$defs": {
        "NonInlinedType": {
            "type": "object",
            "properties": {
                "inner_field": {
                    "type": "string"
                }
            }
        }
    }
  }
  ```

All of these representations are non-idiomatic. In JSON Schema,
explicitly defining a field as `null` is **_not_** equivalent to not
specifying the field. We should only permit specifying a field as
`null` when this is semantically accurate, not as shorthand for
"not defined."

We control whether a property is mandatory in the schema with other
keywords, like `required` and `dependentRequired`.

This change:

- Adds the `idiomaticize_option_field` transform to address this
  limitation of schemars for a single field or variant.
- Adds the `idiomaticize_optional_properties` transform to address this
  limitation of schemars at the top level of a struct, applying the
  `idiomaticize_option_field` transform to every optional field in the
  struct.
- Adds new helpers to `SchemaUtilityExtensions` for ease of use in the
  implementation of the new transforms:

  - `get_defined_keywords` to return every keyword defined at the top
    level of the schema.
  - `get_properties_keys` to return the keys of the `properties` object
    at the top level of the schema.
  - `get_required_property_names` to return the names of the properties
    defined in the `required` keyword at the top level of the schema.
- Includes documentation and integration testing

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new public transforms have a documentation/behavior mismatch and can panic unexpectedly when applied to non-schemars “optional but non-nullable” schemas.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds schema transforms in dsc-lib-jsonschema to “idiomaticize” Option<T> handling from schemars by removing explicit null branches and instead relying on required to express optionality, plus new schema utility helpers and integration tests.

Changes:

  • Introduces idiomaticize_option_field (field-level) and idiomaticize_optional_properties (struct-level) transforms.
  • Adds SchemaUtilityExtensions helpers for top-level keyword discovery and property/required key enumeration.
  • Adds integration tests and localized panic messages for the new transform behavior.
File summaries
File Description
lib/dsc-lib-jsonschema/tests/integration/transforms/mod.rs Registers new transform integration test modules.
lib/dsc-lib-jsonschema/tests/integration/transforms/idiomaticize_optional_properties.rs Adds integration coverage for struct-level optional-property idiomaticization.
lib/dsc-lib-jsonschema/tests/integration/transforms/idiomaticize_option_field.rs Adds integration coverage for field-level Option<T> idiomaticization patterns.
lib/dsc-lib-jsonschema/src/transforms/mod.rs Exposes the two new transforms from the transforms module.
lib/dsc-lib-jsonschema/src/transforms/idiomaticize_optional_properties.rs Implements struct-level transform over non-required properties.
lib/dsc-lib-jsonschema/src/transforms/idiomaticize_option_field.rs Implements field-level transform to remove null branches from schemars Option<T> schemas.
lib/dsc-lib-jsonschema/src/schema_utility_extensions.rs Adds new schema helper methods used by the transforms.
lib/dsc-lib-jsonschema/locales/en-us.toml Adds i18n strings for new transform panic messages.
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/dsc-lib-jsonschema/src/transforms/idiomaticize_option_field.rs Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@SteveL-MSFT
Steve Lee (SteveL-MSFT) added this pull request to the merge queue Sep 3, 2026
Merged via the queue into PowerShell:main with commit fc48c5c Sep 3, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants