Skip to content

OpenAPI ↔ Rust (actix, diesel)

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

offscale/cdd-rust

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cdd-rust: OpenAPI ↔ Rust

Rust: nightly License: (Apache-2.0 OR MIT) CI

cdd-rust is a compiler-driven development toolchain designed to enable "Surgical" Compiler-Driven Development.

Unlike traditional generators that blindly overwrite files or dump code into "generated" folders, cdd-rust understands the Abstract Syntax Tree (AST) of your Rust code. It uses ra_ap_syntax (the underlying parser of rust-analyzer) to read, understand, and safely patch your existing source code to match your OpenAPI specifications (and vice-versa).

⚡️ Key Capabilities

1. Surgical Merging (OpenAPI ➔ Existing Rust)

The core engine features a non-destructive patcher (cdd-core/patcher) capable of merging OpenAPI definitions into an existing codebase.

  • AST-Aware: It preserves your comments, whitespace, and manual formatting.
  • Smart Routing: It can parse your existing actix_web configuration functions and inject missing .service() calls without duplicating existing ones.
  • Type Safety: OpenAPI types are strictly mapped to Rust constraints:
    • format: uuiduuid::Uuid
    • format: date-timechrono::DateTime<Utc>
    • format: passwordSecret<String>
  • Typed Queries: Query parameters are generated as dedicated structs (with serde renames) and injected into handler signatures for strong typing.

2. Synchronization (Database ➔ Rust ➔ OpenAPI)

Keep your code as the single source of truth. The sync workflow ensures your Rust models match your Postgres database and are ready for OpenAPI generation.

  • DB Inspection: Uses dsync to generate strictly typed Diesel structs from the DB schema.
  • Attribute Injection: Automatically parses generated structs to inject #[derive(ToSchema, Serialize, Deserialize)] and necessary imports, ensuring compatibility with utoipa.

3. Contract Verification (OpenAPI ➔ Tests)

Generate strictly typed integration tests (tests/api_contracts.rs) that treat your application as a black box to verify compliance with the spec.

  • Smart Mocking: Automatically creates dummy values for Path, Query, and Body parameters based on strict type definitions.
  • Non-JSON Bodies: Serializes application/x-www-form-urlencoded, multipart/*, text, and binary request bodies using OAS encoding rules (examples and encoding metadata respected).
  • Runtime Expression Resolution: supports OAS 3.2 runtime expressions and embedded templates (e.g., {$request.body#/id} and https://example.com?x={$request.path.id}) for HATEOAS link validation.
  • Schema Validation: Verifies that API responses strictly match the JSON Schema defined in your OpenAPI document using jsonschema.
  • Status & Header Checks: Fails tests on undocumented status codes and validates response headers against primitive schema types (integer/number/boolean) when specified.

⚡️ The CDD Loop

%%{init: { 
  'theme': 'base', 
  'themeVariables': { 
    'primaryColor': '#ffffff', 
    'primaryTextColor': '#20344b', 
    'primaryBorderColor': '#20344b', 
    'lineColor': '#20344b', 
    'fontFamily': 'Google Sans, sans-serif' 
  } 
}}%% 

graph TD
%% --- Section 1: Route Logic --- 

%% Node: OpenAPI Path
    OasPath("<strong>OpenAPI Path (YAML)</strong><br/>/users/{id}:<br/>&nbsp;&nbsp;get:<br/>&nbsp;&nbsp;&nbsp;&nbsp;parameters:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- name: id<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in: path<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;schema: {format: uuid}"):::yellow

%% Node: Actix Handler
    Actix("<strong>Handler (Rust)</strong><br/>async fn get_user(<br/>&nbsp;&nbsp;id: web::Path&lt;Uuid&gt;<br/>) -> impl Responder {<br/>&nbsp;&nbsp;/* Logic */<br/>}"):::blue

%% Flow: Down (Scaffold) & Up (Reflect) 
    OasPath ==>|"1. SCAFFOLD / PATCH<br/>(Injects Handler & Route Signature)"| Actix
    Actix -.->|"2. REFLECT / GENERATE<br/>(Extracts Paths via AST)"| OasPath

%% --- Spacer to force vertical layout --- 
    Actix ~~~ OasSchema

%% --- Section 2: Data Models --- 

%% Node: OpenAPI Schema
    OasSchema("<strong>OpenAPI Schema (YAML)</strong><br/>components:<br/>&nbsp;&nbsp;schemas:<br/>&nbsp;&nbsp;&nbsp;&nbsp;User:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type: object<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;email: {type: string}"):::yellow

%% Node: Diesel Model
    Diesel("<strong>Data Model (Rust)</strong><br/>#[derive(ToSchema)]<br/>struct User {<br/>&nbsp;&nbsp;id: Uuid,<br/>&nbsp;&nbsp;email: String<br/>}"):::green

%% Flow: Down (Scaffold) & Up (Reflect) 
    OasSchema ==>|"1. SCAFFOLD / PATCH<br/>(Injects Fields & Types)"| Diesel
    Diesel -.->|"2. REFLECT / GENERATE<br/>(Derives Attributes)"| OasSchema

%% --- Styles --- 
    classDef yellow fill:#f9ab00,stroke:#20344b,stroke-width:2px,color:#ffffff,font-family:'Consolas',font-size:14px,text-align:left; 
    classDef blue fill:#4285f4,stroke:#20344b,stroke-width:2px,color:#ffffff,font-family:'Consolas',font-size:14px,text-align:left; 
    classDef green fill:#34a853,stroke:#20344b,stroke-width:2px,color:#ffffff,font-family:'Consolas',font-size:14px,text-align:left; 

    linkStyle default stroke:#20344b,stroke-width:2px; 
Loading

🏗 Architecture

The internal architecture separates the core AST/OpenAPI parsing logic from the target code generation. This allows the tool to support multiple web frameworks and ORMs through the BackendStrategy and ModelMapper traits.

graph LR
%% --- NODES --- 
    InputOAS(<strong>OpenAPI Spec</strong><br/><em>YAML</em>)
    InputSrc(<strong>Rust Source</strong><br/><em>Files / Schema</em>)

    subgraph Core [Layer 1: Core]
        direction TB
        P_OAS(<strong>OAS Parser</strong><br/><em>serde_yaml</em>)
        P_AST(<strong>AST Parser</strong><br/><em>ra_ap_syntax</em>)
    end

    subgraph Analysis [Layer 2: Analysis]
        IR(<strong>Intermediate Representation</strong><br/><em>ParsedRoute / ParsedStruct</em>)
    end

    subgraph Gen [Layer 3: Generation]
        Base(<strong>Generator Engine</strong><br/><em>Traits: BackendStrategy & ModelMapper</em>)

    %% The Fork
        subgraph Targets [Targets]
            direction TB
            T_Actix(<strong>Actix</strong><br/><em>ActixStrategy</em>)
            T_Diesel(<strong>Diesel</strong><br/><em>DieselMapper</em>)
            T_OutputOAS(<strong>OpenAPI</strong><br/><em>Spec Generation</em>)
            T_Future(<strong>Axum / SQLx</strong><br/><em>Future Strategies</em>)
        end
    end

%% --- EDGES --- 
    InputOAS --> P_OAS
    InputSrc --> P_AST

    P_OAS --> IR
    P_AST --> IR

    IR --> Base

    Base -- "Scaffold / Test" --> T_Actix
    Base -- "Sync Models" --> T_Diesel
    Base -- "Reflect" --> T_OutputOAS
    Base -. "Extension" .-> T_Future

%% --- STYLING --- 
    classDef blue fill:#4285f4,stroke:#ffffff,color:#ffffff,stroke-width:0px
    classDef yellow fill:#f9ab00,stroke:#ffffff,color:#20344b,stroke-width:0px
    classDef green fill:#34a853,stroke:#ffffff,color:#ffffff,stroke-width:0px
    classDef white fill:#ffffff,stroke:#20344b,color:#20344b,stroke-width:2px
    classDef future fill:#f1f3f4,stroke:#20344b,color:#20344b,stroke-width:2px,stroke-dasharray: 5 5

    class InputOAS,InputSrc white
    class P_OAS,P_AST blue
    class IR yellow
    class Base green
    class T_Actix,T_Diesel,T_OutputOAS white
    class T_Future future
Loading

The project is workspace-based to separate core logic from the command-line interface.

Crate Purpose
cdd-core The Engine. Contains the ra_ap_syntax parsers, the OpenAPI 3.x parser (with 3.2 shims), AST diffing logic, and the Backend Strategy traits (currently implementing ActixStrategy).
cdd-cli The Interface. Provides the sync and test-gen commands.
cdd-web The Reference. An Actix+Diesel implementation demonstrating the generated code and tests in action.

📦 CLI Usage

1. Sync Pipeline (DB ➔ Models)

Synchronize Diesel models and inject OpenAPI attributes.

cargo run -p cdd-cli -- sync \
  --schema-path web/src/schema.rs \
  --model-dir web/src/models

This performs the following:

  1. Reads schema.rs.
  2. Generates rust structs in models/ using diesel/dsync logic.
  3. Patches the files to add #![allow(missing_docs)], use utoipa::ToSchema;, and derive macros.

2. Test Generation (OpenAPI ➔ Tests)

Scaffold integration tests to verify your implementation meets the contract.

cargo run -p cdd-cli -- test-gen \
  --openapi-path docs/openapi.yaml \
  --output-path web/tests/api_contracts.rs \
  --app-factory crate::create_app

This generates a test file that:

  1. Initializes your App factory.
  2. Iterates through every route in your OpenAPI spec.
  3. Sensibly mocks requests.
  4. Validates that your Rust implementation returns the headers and bodies defined in the YAML.

3. Schema Generation (Rust ➔ OpenAPI)

Emit a minimal OpenAPI 3.2 document from a Rust struct/enum with optional info metadata.

cargo run -p cdd-cli -- schema-gen \
  --source-path web/src/models/user.rs \
  --name User \
  --openapi \
  --info-title "User API" \
  --info-version "1.0.0" \
  --info-summary "User service schema" \
  --info-terms-of-service "https://example.com/terms" \
  --info-contact-name "API Support" \
  --info-contact-email "support@example.com" \
  --info-license-name "Apache 2.0" \
  --info-license-identifier "Apache-2.0"

🛠 OpenAPI Compliance

cdd-rust features a highly compliant custom parser found in core/src/oas.

  • Versions: Supports OpenAPI 3.0 and 3.1 directly.
  • Compatibility: Implements shims for OpenAPI 3.2, specifically handling the $self keyword for Base URI determination (Appendix F) and downgrading version strings for library compatibility.
  • Relative $self: Resolves relative $self values (including dot-segment normalization) when matching local $ref targets and operationRef pointers.
  • Validation: Enforces required info, URI/email formatting for Info/Contact/License, leading-slash paths, unique operationId values, templated path conflicts, component key naming rules, response status code keys, security scheme definitions, security requirement resolution, non-empty response.description and requestBody.content, mutual exclusivity of example vs examples for parameters/headers, sequential-only use of itemSchema, and rejects additionalOperations that reuse reserved HTTP methods.
  • Resolution: Local $ref resolution plus base-URI-aware absolute/relative self-references (no external fetch).
  • Multi-Document $ref: Optional DocumentRegistry resolves external OpenAPI/Schema documents (path items, parameters, request bodies, responses, headers, links, media types, and security schemes) plus schema $id/$anchor targets.
  • Schema $id References: Resolves schema $ref values that match component or inline $id URIs (absolute or resolved against $self).
  • Schema Anchors: Resolves $anchor / $dynamicAnchor targets for component or inline schemas and resolves $dynamicRef using dynamic anchor scope (falling back to standard $ref resolution).
  • Relative Server URLs: Resolves relative servers.url values (e.g., ., ./v1, v1) into base paths with RFC3986 dot-segment normalization, using the retrieval URI when provided.
  • Polymorphism: handles oneOf, anyOf, and allOf (flattening) into Rust Enums and Structs.
  • Discriminator Defaults: supports discriminator.defaultMapping for OAS 3.2 polymorphic schemas.
  • Extractors: Maps OAS parameters to backend-specific extractors (e.g., web::Query, web::Path, web::Json, SecurityScheme).
  • Media Types: Recognizes vendor +json media types, text/*, and binary request bodies with dedicated extractors.
  • Media Type References: Resolves components.mediaTypes $ref entries inside content (request bodies, responses, and headers).
  • Parameter Content Media Types: Resolves components.mediaTypes $ref in parameter content, supports itemSchema for sequential media types, and honors serializedValue/externalValue examples.
  • Content Schema Mapping: Uses contentSchema (for JSON-encoded string payloads) to drive strong typing for parameters and request bodies.
  • Sequential Multipart: supports multipart/mixed and multipart/byteranges itemSchema normalization.
  • Positional Encoding: parses prefixEncoding / itemEncoding for multipart media types (OAS 3.2).
  • Nested Encoding: parses and emits nested Encoding Object encoding / prefixEncoding / itemEncoding fields.
  • Examples: Uses parameter content examples when generating contract tests, and honors dataValue / serializedValue / externalValue for parameters and request bodies (serialized examples bypass re-encoding).
  • Sequential Media Types: Supports itemSchema for sequential JSON request bodies (e.g., jsonl, ndjson) and maps to Vec<T>.
  • Sequential Response Types: Uses itemSchema for sequential media types (including text/event-stream and multipart/*) to infer Vec<T> when no schema is present.
  • Sequential Vendor Suffixes: Treats +jsonl, +ndjson, and +json-seq media types as sequential for itemSchema typing.
  • Response Validation: Contract tests validate JSON, vendor +json, sequential JSON, and text/event-stream responses.
  • Response Headers: Resolves response header $ref and content definitions when extracting response metadata.
  • Response Header Content Priority: Prefers content (or preserved x-cdd-content) over injected schema when parsing response header media types, ensuring content-based headers round-trip correctly.
  • Response Header Content Round-Trip: Preserves header content media types (and examples) when generating OpenAPI.
  • Response Summary: Preserves Response Object summary during parsing and OpenAPI generation (including callbacks).
  • Schema Dialects: Contract tests select JSON Schema draft via jsonSchemaDialect or per-schema $schema (defaults to Draft 2020-12 for OAS 3.1/3.2, Draft 4 for OAS 3.0/Swagger 2.0).
  • Header Arrays/Objects: Contract tests validate schema-based header arrays/objects using style: simple (explode and non-explode).
  • Reference Overrides: Honors Reference Object description overrides for responses during validation and resolution.
  • Set-Cookie Handling: Validates Set-Cookie headers without comma-splitting, preserving cookie values that contain commas.
  • Querystring: Serializes querystring params as JSON when the media type is application/json (RFC3986-encoded).
  • Querystring Extractors: Non-form querystring parameters are surfaced as raw String values in generated handlers to avoid incorrect form decoding; form-encoded querystrings remain typed.
  • Header/Cookie Params: Contract tests serialize header/cookie parameters using OAS style/explode rules or content media types.
  • Header Validation: Enforces Header Object constraints (schema vs content, style: simple, no allowEmptyValue), and ignores Content-Type headers.
  • Media Type Examples: Validates example vs examples mutual exclusivity in request/response content.
  • Component Media Types: Validates components.mediaTypes example conflicts and encoding field compatibility.
  • Serialized/External Examples: Preserves serializedValue and externalValue examples when generating OpenAPI for parameters and request bodies.
  • Example Metadata: Preserves Example Object summary and description when round-tripping examples.
  • Example Ref Overrides: Applies Reference Object summary/description overrides when resolving Example $ref values.
  • Boolean Schemas: Handles schema: true/false in request/response bodies and response headers (rejects required false bodies).
  • Nullable Normalization: Converts nullable / x-nullable into type: [..., "null"] for OpenAPI → Rust typing.
  • Byte/Binary Formats: Maps format: byte / format: binary to Vec<u8> in OpenAPI → Rust type mapping.
  • Boolean Parameter Schemas: Accepts schema: true for parameters (maps to String) and rejects schema: false.
  • Example Precedence: Honors serializedValue / externalValue examples for parameters even when value is present.
  • Style Validation: Enforces type constraints for deepObject, spaceDelimited, and pipeDelimited parameter styles.
  • Schema ExternalDocs: emits externalDocs metadata when generating OpenAPI schemas from Rust models.
  • Schema ExternalDocs Validation: Validates externalDocs.url on schema objects (including nested schemas).
  • Schema XML Validation: Validates schema xml objects, including nodeType values and conflicts with deprecated attribute / wrapped fields.
  • Serde Mapping: respects rename_all and deny_unknown_fields when generating OpenAPI schemas from Rust models.
  • Link Servers: Applies Link Object server overrides (including defaulted variables) when generating HATEOAS link construction code.
  • Link Server Metadata: Preserves Link Object server details (name/description/variables) when generating OpenAPI.
  • Link Validation: Resolves operationRef pointers to concrete path+method targets and errors on unknown operationId links.
  • Link Components: Resolves operationRef pointers to components.pathItems when the component is referenced by a unique path (errors on ambiguity).
  • Link Round-Trip: Resolves operationId/operationRef targets for codegen without mutating the original Link Object, avoiding invalid operationId+operationRef emissions during OpenAPI generation.
  • Link Validation (Local Ref): Errors when a local operationRef fails to resolve to a known operation.
  • Link Object Validation: Enforces exactly one of operationId or operationRef, validates Link Object server definitions, enforces link name key patterns, and detects link $ref cycles.
  • Link Parameter Keys: Validates qualified Link parameters keys (e.g., path.id) and substitutes them into link templates using the unqualified parameter name.
  • Link Parsing: Accepts normalized snake_case link keys (operation_id, operation_ref, request_body) during YAML preprocessing.
  • OAuth2/OIDC Schemes: Preserves OAuth2 flows (including device authorization) and OpenID Connect discovery URLs when generating OpenAPI.
  • Contract Tests: Skips webhook routes (inbound) during test generation.
  • Contract Tests (Multipart): Builds multipart request payloads from example values and per-part contentType hints.
  • Contract Tests (Encoding Headers): Applies Encoding object headers to multipart parts (excluding Content-Type) and respects Encoding.contentType for JSON-form-urlencoded fields.
  • Encoding Header Refs: Resolves Encoding.headers $ref entries against components.headers when extracting request body definitions.
  • Callbacks: Enforces operationId uniqueness across callbacks and top-level operations.
  • Callback Parameters: Parses callback Path Item and Operation parameters, merging them for callback operations and emitting them during OpenAPI generation.
  • Top-Level Security: Preserves root-level security requirements when generating OpenAPI documents.
  • Operation Security Overrides: Preserves explicit operation-level security (including empty arrays) without leaking global security into each operation during OpenAPI round-trips.
  • Callback Security: Parses and emits callback operation security requirements (including inherited defaults).
  • Header Parameter Case: Treats header parameter names as case-insensitive when detecting duplicates.
  • OpenAPI Generation: Emits top-level tags and servers (or per-operation servers) from parsed routes.
  • Round-Trip Metadata: Preserves original operationId values and selected response status/description/media type in generated OpenAPI (including callbacks).
  • Document Metadata Round-Trip: parse_openapi_document extracts $self, jsonSchemaDialect, info, servers, tags, externalDocs, and root-level x- extensions for OpenAPI → Rust → OpenAPI workflows.
  • Component Preservation: Can merge existing components (responses, examples, mediaTypes, etc.) when generating OpenAPI.
  • Schema Keyword Passthrough: Preserves advanced JSON Schema keywords (if/then/else, dependentSchemas, unevaluatedProperties, contentSchema) across parameters, request/response bodies, and component schemas during OpenAPI ↔ Rust round-trips.
  • Paths/Webhooks Extensions: Accepts x- keys at the paths and webhooks object level without treating them as path items, and preserves them during OpenAPI generation.
  • Path Item Extensions: Preserves x- extensions on Path Item Objects during OpenAPI ↔ Rust round-trips.
  • Server Override Round-Trip: Preserves path/operation-level servers objects (including variables) instead of collapsing to base paths.
  • Server URL Validation: Rejects invalid URI references in servers.url (including whitespace in literal segments).

Developer Guide

Prerequisites

  • Rust (Nightly toolchain required for ra_ap_syntax compatibility feature flags in some contexts).
  • PostgreSQL (if running the reference web implementation).

Installation

git clone https://github.com/offscale/cdd-rust
cd cdd-rust
cargo build

Running Tests

# Run unit tests
cargo test

# Run the generated contract tests (requires web/tests/api_contracts.rs to be generated)
cargo test -p cdd-web

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.


Developer guide

Install the latest version of Rust. We tend to use nightly versions. CLI tool for installing Rust.

We use rust-clippy linters to improve code quality.

Step-by-step guide

# Install Rust (nightly) 
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain nightly
# Install cargo-make (cross-platform feature-rich reimplementation of Make) 
$ cargo install --force cargo-make
# Install rustfmt (Rust formatter) 
$ rustup component add rustfmt
# Clone this repo
$ git clone https://github.com/offscale/cdd-rust && cd cdd-rust
# Run tests
$ cargo test
# Format, build and test
$ cargo make

License

Licensed under either of

at your option.

About

OpenAPI ↔ Rust (actix, diesel)

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages