oas-forge 0.1.0

The zero-runtime OpenAPI 3.1 compiler for Rust. Extracts, links, and merges code-first documentation.
Documentation

oas-forge

Crates.io Docs.rs License Build Status Latest Release

The zero-runtime OpenAPI 3.1 compiler for Rust.

oas-forge extracts, links, and merges code-first documentation into a standard openapi.yaml file at compile time. It eliminates the need for runtime macros that bloat your binary and crash on startup.

Architecture

[Source Code] --> [Scanner] --> [AST Parsing] --> [Registry]    
                                                     |
                                                     v
[Static YAML] --> [Merger] <--- [Monomorphizer] <--- [Fragments]
                       |
                       v
                 [openapi.yaml]

Integration Guide

Method A: build.rs (Fluent API)

The recommended approach.

use oas_forge::Generator;

fn main() {
    Generator::new()
        .input("src")
        .include("static/security.yaml") // Merge static config
        .output("openapi.yaml")
        .generate()
        .expect("Failed to generate OpenAPI spec");
}

Method B: Cargo.toml Metadata

Configure in your manifest to keep build.rs minimal.

Cargo.toml:

[package.metadata.oas-forge]
input = ["src", "lib"]
include = ["static/skeleton.yaml"]
output = "docs/api.yaml"

[build-dependencies]
oas-forge = "0.4"

build.rs:

fn main() {
    oas_forge::Generator::default().generate().unwrap();
}

Method C: CLI

Ideal for CI/CD pipelines.

cargo install oas-forge
# Run generation
oas_forge -i src -I static/openapi.yaml -o openapi.yaml

Feature Reference

⚡ Route DSL

The Route DSL works purely in doc comments.

Inline Path Parameters (The "New Way")

Define parameters, types, and descriptions directly in the path.

/// @route GET /actress/{id: u32 "The unique ID"}
fn get_actress(id: u32) { ... }
  • Result: Automatically registers id as in: path, required: true, with schema: {type: integer, format: int32}.

Legacy Path Parameters (The "Old Way")

You can still split them if you prefer.

/// @route GET /actress/{id}
/// @path-param id: u32 "The unique ID"
fn get_actress(id: u32) { ... }

Flexible Parameter Syntax

Attributes are order-independent. Usage: Name: [Type] [Flags...] [Desc].

/// @route GET /search
/// @query-param filter: Option<String> deprecated example="Alice" "Filter by name"
/// @header-param X-Trace-ID: Uuid required "Tracing ID"
fn search() { ... }
  • deprecated: Sets deprecated: true.
  • example="Alice": Sets example: "Alice".
  • Option<T>: Infers required: false (unless required flag is present).

Request Body

Link a Struct as the request body.

/// @route POST /users
/// @body $CreateRequest application/json
fn create() { ... }

Smart Responses

The DSL infers schemas and handles generics.

/// Creates a new user.
/// 
/// @route POST /users
/// @return 201: $User "Created"          <- JSON Ref to User schema
/// @return 200: $Page<User> "List"       <- Monomorphized Generic ($Page<User> -> Page_User)
/// @return 204: "Deleted"                <- Unit Type (No Content body)
/// @return 400: "Invalid Input"          <- String Literal (Description only)
fn create_user() { ... }

📦 Type Reflection

Document your Rust types to generate JSON Schemas.

Structs

/// @openapi
/// description: A registered user.
struct User {
    /// @openapi example: "123e4567-e89b..."
    id: Uuid,
    /// @openapi description: "Full name"
    name: String,
}

Enums

Rust Enums are mapped to OpenAPI String Enums.

/// @openapi
enum Role {
    Admin,
    User,
    Guest
}
// output: type: string, enum: ["Admin", "User", "Guest"]

Type Aliases & Newtypes

Fully supported.

/// @openapi
/// format: email
type Email = String;

/// @openapi
struct UserId(pub Uuid);

🛡️ Validation Safety

oas-forge validates your documentation at compile time.

Example Failure:

/// @route GET /users/{id}
/// @path-param slug: String "Slug"
fn get_user() {}

Compiler Error:

error: Missing definition for path parameter 'id' in route '/users/{id}'

License

This project is licensed under the MIT license.