You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
3.5 KiB
Markdown
105 lines
3.5 KiB
Markdown
---
|
|
# quotesdb-5aow
|
|
title: Add build.rs — convert api/openapi.yaml to JSON at compile time for Workers embed
|
|
status: completed
|
|
type: task
|
|
priority: critical
|
|
created_at: 2026-03-10T23:32:08Z
|
|
updated_at: 2026-03-10T23:32:08Z
|
|
---
|
|
|
|
<context>
|
|
Resolved from TRIAGE ticket 2ec8b1. The `GET /api/` endpoint must serve the OpenAPI spec as JSON.
|
|
|
|
The three strategies were:
|
|
1. Compile-time embed (chosen)
|
|
2. Runtime load from filesystem — impossible on Cloudflare Workers (no filesystem)
|
|
3. utoipa programmatic generation — significant complexity; spec already exists and is complete
|
|
|
|
The chosen approach: a `build.rs` script reads `api/openapi.yaml`, parses it to a
|
|
`serde_json::Value`, serialises it as compact JSON, and writes the result to
|
|
`$OUT_DIR/openapi.json`. The `GET /api/` handler then serves this via:
|
|
|
|
```rust
|
|
const OPENAPI_JSON: &str = include_str!(concat!(env!("OUT_DIR"), "/openapi.json"));
|
|
```
|
|
|
|
This means:
|
|
- `serde_yaml` ships only as a `[build-dependencies]` entry — it never enters the Workers binary.
|
|
- The handler is a zero-overhead static string response with no runtime parsing.
|
|
- `cargo:rerun-if-changed=api/openapi.yaml` ensures the conversion re-runs whenever the spec
|
|
is edited — no manual JSON regeneration step needed.
|
|
- `api/openapi.yaml` remains the single source of truth; the JSON output is ephemeral (in
|
|
`$OUT_DIR`, not committed to the repository).
|
|
</context>
|
|
|
|
<goal>
|
|
1. Create `build.rs` at the `quotesdb/` project root containing:
|
|
|
|
```rust
|
|
use std::{env, fs, path::Path};
|
|
|
|
fn main() {
|
|
// Re-run this script whenever the OpenAPI spec changes.
|
|
println!("cargo:rerun-if-changed=api/openapi.yaml");
|
|
|
|
let yaml =
|
|
fs::read_to_string("api/openapi.yaml").expect("api/openapi.yaml not found");
|
|
|
|
// Parse YAML to a generic JSON value, then re-serialise as compact JSON.
|
|
// serde_yaml is a build-only dependency — it does not appear in the final binary.
|
|
let value: serde_json::Value =
|
|
serde_yaml::from_str(&yaml).expect("api/openapi.yaml is invalid YAML");
|
|
let json = serde_json::to_string(&value).expect("JSON serialisation failed");
|
|
|
|
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
|
|
let out_path = Path::new(&out_dir).join("openapi.json");
|
|
fs::write(&out_path, json).expect("failed to write openapi.json");
|
|
}
|
|
```
|
|
|
|
2. Add the following to `Cargo.toml` (ticket 1f5bb5 should also include this):
|
|
|
|
```toml
|
|
[build-dependencies]
|
|
serde_json = "1"
|
|
serde_yaml = "0.9"
|
|
```
|
|
|
|
3. Verify the build succeeds and `$OUT_DIR/openapi.json` is produced:
|
|
|
|
```sh
|
|
cargo check
|
|
# $OUT_DIR is typically target/debug/build/quotesdb-*/out/openapi.json
|
|
```
|
|
</goal>
|
|
|
|
<constraints>
|
|
- `serde_yaml` must be a `[build-dependencies]` entry only — NOT in `[dependencies]`.
|
|
Adding it to `[dependencies]` would bloat the Workers WASM binary.
|
|
- Do NOT commit `$OUT_DIR/openapi.json` — it is generated automatically at build time.
|
|
- The `build.rs` file lives at the crate root (same level as `Cargo.toml`), not in `src/`.
|
|
- `api/openapi.yaml` is the source of truth; do not create or commit an `api/openapi.json`.
|
|
</constraints>
|
|
|
|
<downstream>
|
|
Ticket 28e7d9 (GET /api/ handler) depends on this ticket. The handler uses
|
|
`include_str!(concat!(env!("OUT_DIR"), "/openapi.json"))` to serve the spec — see 28e7d9
|
|
for the Axum handler implementation.
|
|
</downstream>
|
|
|
|
<validation>
|
|
```sh
|
|
cargo fmt
|
|
cargo check
|
|
cargo clippy
|
|
cargo test
|
|
```
|
|
</validation>
|
|
|
|
<commit>
|
|
`chore(quotesdb): add build.rs to convert api/openapi.yaml to JSON at compile time`
|
|
</commit>
|
|
|
|
<domain>quotesdb/api</domain>
|