feat: implement readonly and writeonly - #128
Conversation
There was a problem hiding this comment.
Pull request overview
Implements OpenAPI 3.0 readOnly / writeOnly semantics by treating them as directional markers (request vs response) and projecting models into request/response shapes, including split type naming (<Name>Request / <Name>Response) when a model is used in both directions (Issue #105).
Changes:
- Introduces direction-aware model projection via a new lowering pass that splits and rewrites models and service type references based on request/response reachability.
- Adds IR support for direction and per-field access (
readOnly/writeOnly) and threads it through schema lowering and multipart lowering (including rejectingreadOnly && writeOnly). - Updates documentation, fixtures, generated snapshots, and tests/coverage to validate the new behavior (including cross-file import-mapping rejection for split targets).
Reviewed changes
Copilot reviewed 19 out of 22 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| docs/design.md | Documents the direction-projection model and naming/splitting rules for readOnly/writeOnly. |
| crates/oapi-codegen/tests/generated/server_auth.rs | Updates generated doc comment style output for multi-line descriptions. |
| crates/oapi-codegen/tests/generated/read_write_only.rs | Adds generated snapshot demonstrating request/response shape splitting for marked properties. |
| crates/oapi-codegen/tests/generated/combined_read_write_only.rs | Adds combined server/client generated snapshot exercising split models in operations, multipart, client, and server code. |
| crates/oapi-codegen/tests/generated.rs | Registers new generated snapshots and adds behavioral tests for split request/response shapes. |
| crates/oapi-codegen/tests/fixtures/server_unsupported_xfile_direction_split.yaml | Adds fixture for rejecting cross-file $ref into a schema that would split. |
| crates/oapi-codegen/tests/fixtures/schemas/compose_marked.yaml | Adds shared-schema fixture that triggers splitting to validate import-mapping rejection. |
| crates/oapi-codegen/tests/fixtures/read_write_only.yaml | Adds fixture for base direction-splitting behavior (struct, holder, alias, untouched). |
| crates/oapi-codegen/tests/fixtures/read_write_only_conflict.yaml | Adds fixture validating rejection when both marks are set. |
| crates/oapi-codegen/tests/fixtures/combined_read_write_only.yaml | Adds fixture covering direction behavior in a full API (request bodies, responses, multipart). |
| crates/oapi-codegen/tests/coverage.rs | Marks meta.readOnly/meta.writeOnly as supported and adds unsupported conflict + fixture coverage. |
| crates/oapi-codegen/src/lower/schema.rs | Lowers readOnly/writeOnly into per-field Access (including $ref targets) and rejects conflicting marks. |
| crates/oapi-codegen/src/lower/recurse.rs | Updates tests to account for the new Field.access field. |
| crates/oapi-codegen/src/lower/paths.rs | Ensures multipart extractor drops readOnly parts and threads direction access checks into multipart lowering. |
| crates/oapi-codegen/src/lower/mod.rs | Exposes the new direction module and re-exports split_by_direction. |
| crates/oapi-codegen/src/lower/direction.rs | New lowering pass implementing model/service projection and type rewriting for request/response shapes. |
| crates/oapi-codegen/src/loader.rs | Rejects import-mapping references to schemas that would split and adds a same-document scan to detect them. |
| crates/oapi-codegen/src/lib.rs | Integrates direction splitting into the main lowering pipeline (before prune and name checks) and models-only generation. |
| crates/oapi-codegen/src/ir.rs | Adds Direction and Access enums and the Field.access field to the IR. |
| crates/oapi-codegen/src/emit/usage.rs | Exposes direction usage data (direction_usage, adjacency, Usage) needed by the direction pass. |
| crates/oapi-codegen/src/emit/models.rs | Updates tests for the new Field.access field in test fixtures. |
| crates/oapi-codegen/src/emit/mod.rs | Improves doc emission to split multi-line descriptions into multiple /// lines. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
crates/oapi-codegen/src/lower/direction.rs:282
- This doc comment says
project_service“drop[s] the multipart parts a request must not send”, but this function only rewrites types; multipart field dropping happens earlier during lowering (multipart_part_is_sentinlower/paths.rs). Please adjust the comment to avoid misleading future maintainers.
/// Point every API position at the shape of the direction it carries, and drop
/// the multipart parts a request must not send.
crates/oapi-codegen/src/loader.rs:459
direction_split_schemas()treats a component schema withschema_data.read_only/write_onlyset (e.g. a shared primitive schema markedreadOnlyand referenced via$ref) as if it will be emitted as<Name>Request/<Name>Response. The lowering pass only splits models when a struct field ends up withAccess != ReadWrite, so such “schema-level” marks can be used to mark a referencing property without the referenced schema itself being split. As a result,external_schema_name()can reject validimport-mappingreferences and the error message can describe shapes the models run never emits. Consider aligningdirection_split_schemas()with the same “sensitive model” criteria aslower::direction(seed from object properties whose effectiveAccessis notReadWrite, then close over referrers), so only actually-split schemas are rejected here.
let ident = crate::naming::to_ident(&chosen, crate::naming::Case::Pascal);
if direction_split_schemas(&doc).contains(name) {
let shape = ident.logical();
return Err(Error::UnsupportedRef {
reference: reference.to_owned(),
4d4729c to
0340176
Compare
0340176 to
17024b3
Compare
7c3107e to
a1e1ec7
Compare
|
🎉 This PR is included in version 1.2.0-dev.1 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Closes #105