Skip to content

fix: add default support - #89

Merged
dotkas merged 7 commits into
developfrom
dotkas/add-support-for-default
Aug 6, 2026
Merged

fix: add default support#89
dotkas merged 7 commits into
developfrom
dotkas/add-support-for-default

Conversation

@dotkas

@dotkas dotkas commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI lite review requested due to automatic review settings August 5, 2026 16:52

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.

Pull request overview

Adds end-to-end support for OpenAPI default values during lowering and code emission, so missing optional fields/params can deserialize into concrete Rust values (and error out when a default cannot be represented).

Changes:

  • Lower schema and query-parameter default into an IR DefaultValue, and adjust optionality so “optional + default” becomes non-Option<T> (except nullable).
  • Emit #[serde(default = "...")] plus per-struct associated default fns, and introduce a dedicated UnsupportedDefault error for unrenderable defaults.
  • Add fixtures + generated outputs + coverage table updates documenting and testing supported/unsupported defaults.

Reviewed changes

Copilot reviewed 17 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
docs/design.md Documents the new default/Option lowering rules and edge cases (nullable, required fields, unrepresentable defaults).
crates/oapi-codegen/tests/generated/server_query_params.rs Updates generated query struct to use a serde default fn and non-optional limit.
crates/oapi-codegen/tests/generated/schema_defaults.rs New generated output exercising defaults across scalar/enum/empty-collection/nullable cases.
crates/oapi-codegen/tests/generated.rs Registers the new generated module.
crates/oapi-codegen/tests/fixtures/unsupported_default_value.yaml New fixture covering an unrepresentable default (non-empty array).
crates/oapi-codegen/tests/fixtures/server_query_params.yaml Adds a query-parameter default used to drive generation changes.
crates/oapi-codegen/tests/fixtures/schema_defaults.yaml New fixture covering supported default shapes and semantics.
crates/oapi-codegen/tests/coverage.rs Marks meta.default supported and adds an explicit unsupported sub-feature + fixtures; wires new generated test.
crates/oapi-codegen/src/lower/schema.rs Computes declared defaults for object properties, adjusts optionality rules, and lowers defaults into IR fields.
crates/oapi-codegen/src/lower/recurse.rs Updates test field construction for the new Field.default member.
crates/oapi-codegen/src/lower/paths.rs Adds query-parameter default lowering and threads owner name for generated default fn paths.
crates/oapi-codegen/src/lower/mod.rs Exposes new lower::default module.
crates/oapi-codegen/src/lower/default.rs New lowering module that validates/normalizes JSON defaults against Rust types and enum wire values.
crates/oapi-codegen/src/ir.rs Extends Field with default: Option<DefaultValue> and defines DefaultValue.
crates/oapi-codegen/src/error.rs Adds Error::UnsupportedDefault with display + error trait integration.
crates/oapi-codegen/src/emit/usage.rs Updates test helper Field construction for the new default member.
crates/oapi-codegen/src/emit/models.rs Emits serde default attributes + associated default fns and renders DefaultValue into Rust expressions.
crates/oapi-codegen/src/console.rs Routes UnsupportedDefault into hint printing.
crates/oapi-codegen/Cargo.toml Adds serde_json dependency for default handling.

Comment thread crates/oapi-codegen/src/lower/default.rs Outdated
Comment thread crates/oapi-codegen/src/emit/models.rs Outdated
Copilot AI review requested due to automatic review settings August 5, 2026 17:01

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.

Pull request overview

Copilot reviewed 17 out of 19 changed files in this pull request and generated no new comments.

Suppressed comments (2)

crates/oapi-codegen/src/lower/default.rs:64

  • lower_default treats both i32 and i64 defaults via Value::as_i64() with no range check. For an int32 field, a schema default outside the i32 range will currently lower successfully and then fail later as a Rust compile error (literal/type mismatch). Prefer rejecting out-of-range values here so the generator reports a clear UnsupportedDefault error instead of producing uncompilable output.
        RustType::Bool => json.as_bool().map(DefaultValue::Bool),
        RustType::I32 | RustType::I64 => json.as_i64().map(DefaultValue::Int),

docs/design.md:310

  • The example ?limit= is misleading: #[serde(default = ...)] only applies when the parameter is absent, not when it is present but empty. With axum_extra::extract::Query/serde_urlencoded, limit= will typically fail to deserialize into i32 rather than using the default. The text should describe omitting limit entirely.
A query parameter follows the same rule. `?limit=` with `default: 20` gives a
plain `i32` field.

Copilot AI review requested due to automatic review settings August 5, 2026 17:08

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.

Pull request overview

Copilot reviewed 17 out of 19 changed files in this pull request and generated no new comments.

Suppressed comments (3)

docs/design.md:310

  • The example ?limit= is a present-but-empty query parameter, not an absent parameter. A serde default only applies when the key is missing; with ?limit= deserialization of i32 will typically fail rather than falling back to the default. Reword this to describe an absent limit parameter (or use an example that omits limit entirely).
A query parameter follows the same rule. `?limit=` with `default: 20` gives a
plain `i32` field.

crates/oapi-codegen/Cargo.toml:27

  • serde_json is now a normal dependency (needed by non-test code), but it is still listed in [dev-dependencies] as well. Keeping the same crate/version in both places is redundant and can drift over time; tests can use the normal dependency.
serde_json = "1.0.151"

crates/oapi-codegen/src/emit/models.rs:180

  • Default functions are emitted whenever field.default is present, even when the struct does not derive any serde traits (has_serde == false). In that case emit_field suppresses the #[serde(default = "...")] attribute, so the generated default_* functions become unused and can trigger dead_code warnings in the generated crate. Gate emitting default functions (and the impl block) on has_serde.
    let mut defaults = Vec::new();
    for field in &strukt.fields {
        fields.push(emit_field(field, has_serde, &strukt.name)?);
        if let Some(value) = &field.default {
            defaults.push(emit_default_fn(field, value)?);

Copilot AI review requested due to automatic review settings August 5, 2026 17:24

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.

Pull request overview

Copilot reviewed 17 out of 19 changed files in this pull request and generated no new comments.

Suppressed comments (2)

crates/oapi-codegen/src/emit/models.rs:246

  • The generated doc string for default helper functions has a leading space (" The ..."), which will be rendered into rustdoc output and looks accidental. Removing it keeps documentation formatting clean.
    let doc = format!(" The `default` the document gives `{}`.", field.name.logical());

crates/oapi-codegen/src/lower/mod.rs:8

  • lower is exported from the crate (pub mod lower in lib.rs), so making lower::default public also exposes this new internal module as part of the public API. If lower_default is only an implementation detail, keep the module private to avoid committing to it as a supported API surface.
pub mod default;

Copilot AI review requested due to automatic review settings August 6, 2026 10:58

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.

Pull request overview

Copilot reviewed 17 out of 19 changed files in this pull request and generated no new comments.

Suppressed comments (3)

crates/oapi-codegen/src/lower/default.rs:47

  • The hint string for UnsupportedDefault uses line-continuation (\) with indentation, which will embed large runs of whitespace in the emitted hint text. This makes the console hint harder to read/copy.
            hint: format!(
                "`{owner}.{property}` lowers to {}. Give it a `default` of that type, or remove the `default`. \
                 This generator renders a scalar, an enum value, an empty array, and an empty object. \
                 It cannot render a non-empty array or object.",
                describe(ty)

crates/oapi-codegen/src/lower/paths.rs:456

  • query_field already resolves the parameter schema inside query_param_type, but query_param_default resolves/clones the schema again just to read schema_data.default. For inline (non-$ref) parameter schemas, this extra clone/work is avoidable.
    /// The `default` a query parameter's schema declares, if any.
    fn query_param_default(
        &self,
        path: &str,
        method: &str,
        origin: Option<&str>,
        data: &ParameterData,
    ) -> Result<Option<serde_json::Value>> {
        let ParameterSchemaOrContent::Schema(schema) = &data.format else {
            // `query_param_type` rejects a `content` parameter with a better
            // message than this could give.
            return Ok(None);
        };
        let schema = self.resolve_param_schema(path, method, origin, &data.name, schema)?;
        return Ok(schema.schema_data.default);
    }

docs/design.md:306

  • The design doc implies that an “empty object” default works for any object-typed field, but the implementation only supports default: {} when the field lowers to a HashMap (RustType::Map). For struct-shaped objects (named types), {} has no literal form and will be rejected as an unsupported default. Clarifying this avoids misleading spec authors.
Only a value with a literal form works: a string, a number, a boolean, an enum
value, an empty array, and an empty object. A non-empty array, a non-empty
object, and a value of the wrong type are errors, not silent drops. A dropped

Copilot AI review requested due to automatic review settings August 6, 2026 11:30

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.

Pull request overview

Copilot reviewed 17 out of 19 changed files in this pull request and generated no new comments.

@dotkas
dotkas merged commit a415d6b into develop Aug 6, 2026
8 checks passed
@dotkas
dotkas deleted the dotkas/add-support-for-default branch August 6, 2026 11:47
@alchemax-housekeeper

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.0.0-dev.27 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@alchemax-housekeeper

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants