Skip to content

Commit 1db3df6

Browse files
committed
Complete rewrite
1 parent 312a6ab commit 1db3df6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4260
-313
lines changed

.github/workflows/ci-cargo.yml

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,41 @@
1-
on: [push]
2-
31
name: CI
42

3+
on:
4+
push:
5+
branches: [ "master" ]
6+
pull_request:
7+
branches: [ "master" ]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
# Ensure we don't need a real DB for basic unit tests
12+
DATABASE_URL: postgres://user:password@localhost/dummy
13+
514
jobs:
615
build_and_test:
7-
name: Rust project
16+
name: Test & Doc Check
817
runs-on: ubuntu-latest
18+
919
steps:
10-
- uses: actions/checkout@v2
11-
- uses: actions-rs/toolchain@v1
12-
with:
13-
toolchain: stable
14-
- uses: actions-rs/cargo@v1
15-
with:
16-
command: build
17-
args: --release --all-features
18-
- uses: actions-rs/cargo@v1
19-
with:
20-
command: test
20+
- uses: actions/checkout@v6
21+
22+
- name: Set up Rust
23+
run: rustup override set nightly
24+
25+
- name: Check Formatting
26+
run: cargo fmt -- --check
27+
28+
- name: Clippy (Linting)
29+
run: cargo clippy --workspace -- -D warnings
30+
31+
- name: Build Workspace
32+
run: cargo build --verbose --workspace
33+
34+
- name: Run Tests
35+
run: cargo test --verbose --workspace
36+
37+
- name: Check Documentation
38+
# This compiles the docs and respects #![deny(missing_docs)]
39+
run: cargo doc --workspace --no-deps --document-private-items
40+
env:
41+
RUSTDOCFLAGS: "-D warnings"

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
/target
22
/Cargo.lock
33
.idea
4+
.vscode
5+
.env

Cargo.toml

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,7 @@
1-
[package]
2-
name = "cdd-rust"
3-
version = "0.1.0"
4-
edition = "2021"
5-
authors = ["Samuel Marks"]
6-
description = "OpenAPI ↔ Rust"
7-
readme = true
8-
license = "Apache-2.0 OR MIT"
9-
keywords = ["openapi", "actix", "actix-web", "diesel", "rest", "orm"]
10-
categories = [
11-
"development-tools",
12-
"development-tools::testing",
13-
"parser-implementations",
14-
"template-engine",
15-
"web-programming::http-server"
1+
[workspace]
2+
members = [
3+
"core",
4+
"cli",
5+
"web",
166
]
17-
18-
[toolchain]
19-
channel = "nightly" # Needed by diesel
20-
21-
[dependencies]
22-
rowan = { git = "https://github.com/rust-analyzer/rowan", branch = "master" }
23-
24-
[dev-dependencies]
25-
actix_web_mocks = { path = "src/actix_web_mocks" }
26-
diesel_mocks = { path = "src/diesel_mocks" }
7+
resolver = "2"

LICENSE-APACHE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
same "printed page" as the copyright notice for easier
188188
identification within third-party archives.
189189

190-
Copyright 2022 Samuel Marks (for Offscale.io)
190+
Copyright 2022–2025 Samuel Marks (for Offscale.io)
191191

192192
Licensed under the Apache License, Version 2.0 (the "License");
193193
you may not use this file except in compliance with the License.

LICENSE-MIT

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright 2022 Samuel Marks (for Offscale.io)
1+
Copyright 2022–2025 Samuel Marks (for Offscale.io)
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
44

README.md

Lines changed: 135 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,153 @@
1-
cdd-rust
2-
--------
3-
[![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech)
1+
cdd-rust: OpenAPI ↔ Rust
2+
========================
43
[![Rust: nightly](https://img.shields.io/badge/Rust-nightly-blue.svg)](https://www.rust-lang.org)
54
[![License: (Apache-2.0 OR MIT)](https://img.shields.io/badge/LICENSE-Apache--2.0%20OR%20MIT-orange)](LICENSE-APACHE)
65
[![CI](https://github.com/offscale/cdd-rust/actions/workflows/ci-cargo.yml/badge.svg)](https://github.com/offscale/cdd-rust/actions/workflows/ci-cargo.yml)
76

8-
Update: future of this repo remains unclear. Probably restart, delete all [non `.git`] files|folders and do a `cargo init`. One direction—different to the [Python](https://github.com/offscale/cdd-python) and [C](https://github.com/SamuelMarks/cdd-c) [cdd](https://compilers.com.au) implementations—is to go from:
7+
**Compiler Driven Development (CDD)** for Rust.
98

10-
0. SQL to [diesel](https://diesel.rs) ([`diesel::table!`](https://docs.rs/diesel/latest/diesel/macro.table.html));
11-
1. [`diesel::table!`](https://docs.rs/diesel/latest/diesel/macro.table.html) to diesel `struct`s and functions (https://github.com/SamuelMarks/dsync); then
12-
2. Diesel `struct`s to/fro [JSON-schema](https://json-schema.org/specification) via a new custom parser/emitter (compiler) written with https://github.com/rust-lang/rust-analyzer
9+
**cdd-rust** bridges the gap between your **Database**, **Rust Code**, and **OpenAPI Specifications**. Unlike traditional generators that produce untouchable code in a "generated" folder, `cdd-rust` uses advanced AST parsing to surgically patch your *existing* source files, strictly Typed handlers, and integration tests.
1310

14-
Next, realise [JSON-schema](https://json-schema.org/specification) as part of a greater [OpenAPI](https://spec.openapis.org/oas/latest.html), and implement custom [rust-analyzer](https://github.com/rust-lang/rust-analyzer) parser/emitter code to go from/to:
11+
## ⚡️ The CDD Loop
1512

16-
3. [actix](https://actix.rs);
17-
4. tests & mocks;
18-
5. OpenAPI [dynamic: [utoipa](https://github.com/juhaku/utoipa); static: custom].
13+
Automate the repetitive parts of building robust web services with **Actix Web**, **Diesel**, and **Postgres**.
14+
15+
```mermaid
16+
%%{init: {
17+
'theme': 'base',
18+
'themeVariables': {
19+
'primaryColor': '#ffffff',
20+
'primaryTextColor': '#20344b',
21+
'primaryBorderColor': '#20344b',
22+
'lineColor': '#20344b',
23+
'fontFamily': 'Google Sans, sans-serif'
24+
}
25+
}}%%
26+
27+
graph TD
28+
%% Nodes
29+
OpenAPI([OpenAPI]):::yellow
30+
Handlers(Actix Handlers):::blue
31+
Models(Diesel Models):::green
32+
DB[(Database)]:::navy
33+
34+
%% Flow 0 & 1: Downward (Scaffold & Patch)
35+
OpenAPI -->|"0. Scaffold (New) | 1. Patch (Existing)"| Handlers
36+
37+
%% Connection
38+
Handlers -- "Uses Strictly Typed Structs" --> Models
39+
40+
%% Flow 2: Upward (Reflection)
41+
Models -.->|"2. Derives Schema (Reflection)"| OpenAPI
42+
43+
%% Database Sync
44+
DB == "Syncs SQL Columns" ==> Models
45+
46+
%% Styles
47+
classDef yellow fill:#f9ab00,stroke:#20344b,stroke-width:2px,color:#ffffff,font-family:'Google Sans Medium',font-size:16px;
48+
classDef blue fill:#4285f4,stroke:#20344b,stroke-width:2px,color:#ffffff,font-family:'Google Sans Medium',font-size:16px;
49+
classDef green fill:#34a853,stroke:#20344b,stroke-width:2px,color:#ffffff,font-family:'Google Sans Medium',font-size:16px;
50+
classDef navy fill:#20344b,stroke:#20344b,stroke-width:2px,color:#ffffff,font-family:'Google Sans Medium',font-size:16px;
51+
52+
linkStyle default stroke:#20344b,stroke-width:2px;
53+
```
54+
55+
---
56+
57+
## 🚀 The Philosophy
58+
59+
**Compiler Driven Development** solves synchronization issues across language boundaries (Backend $\leftrightarrow$ Frontend $\leftrightarrow$ Docs).
60+
61+
* **Single Source of Truth:** Your Code + Attributes *are* the documentation. Your Database *is* the source of your models.
62+
* **No Magic Folders:** We don't hide code. We generate code you can read, debug, and commit.
63+
* **Lossless Patching:** Powered by `ra_ap_syntax` (Rust Analyzer), we edit your files without breaking your manual formatting or comments.
64+
* **Contract Safety:** Automated updating of contract tests ensures your implementation strictly adheres to the API definition.
65+
66+
---
67+
68+
## 🛠 Features
69+
70+
### 1. Database & Model Sync (`sync`)
71+
Keeps your Rust structs in perfect harmony with your Postgres schema.
72+
* **Diesel Integration:** Wraps `dsync` to generate raw structs from `schema.rs`.
73+
* **Attribute Injection:** Automatically parses generated structs and injects `utoipa::ToSchema`, `serde::Serialize`, and `serde::Deserialize`.
74+
* **Result:** A fully documented, OpenAPI-ready Rust struct generated directly from your DB.
75+
76+
### 2. Contract Test Generation (`test-gen`)
77+
Treats your application as a black box to ensure spec compliance.
78+
* **Route Parsing:** Reads your `openapi.yaml`.
79+
* **Test Scaffolding:** Generates `tests/api_contracts.rs` containing `#[actix_web::test]` functions.
80+
* **Smart Mocking:** Automatically generates dummy values for `Uuid`, `Date`, and primitives to satisfy route parameters during testing.
81+
* **Schema Validation:** Includes logic to validate response bodies against the JSON Schema definitions.
82+
83+
### 3. AST-Based Patching (Core)
84+
The engine under the hood (`cdd-core`) provides intelligent code manipulation:
85+
* **Smart Injection:** Adds fields to structs and lines to functions respecting indentation and comma placement.
86+
* **Structural Diffing:** configuration-aware diffing between target specs and actual implementations.
87+
* **Route Registration:** Injects `cfg.service(...)` calls into your Actix config without disrupting existing logic.
88+
89+
---
90+
91+
## 📦 Usage
92+
93+
### Installation
94+
95+
Clone the repository and build the CLI tool:
96+
97+
```bash
98+
cargo build -p cdd-cli --release
99+
```
100+
101+
### Command Reference
102+
103+
#### 1. Sync Pipeline (DB ➔ Rust ➔ OpenAPI)
104+
Transforms raw Diesel schema files into rich, OpenAPI-compatible Rust structs.
105+
106+
```bash
107+
# Workflow:
108+
# 1. Runs `dsync` to update models from schema.rs
109+
# 2. Patches models with #[derive(ToSchema)]
110+
cargo run -p cdd-cli -- sync \
111+
--schema-path web/src/schema.rs \
112+
--model-dir web/src/models
113+
```
114+
115+
#### 2. Test Configuration (OpenAPI ➔ Tests)
116+
Reads your API definition and writes a Rust test suite.
117+
118+
```bash
119+
cargo run -p cdd-cli -- test-gen \
120+
--openapi-path docs/openapi.yaml \
121+
--output-path web/tests/api_contracts.rs \
122+
--app-factory crate::create_app
123+
```
19124

20125
---
21126

22-
OpenAPI ↔ Rust. Compiler Driven Development (CDD) is a new development methodology, with implementations in many languages.
127+
## 🏗 Project Structure
23128

24-
The central idea is to statically code-generate from target language to OpenAPI, and from OpenAPI back to target language.
25-
All without having an untouchable 'generated' directory and without requiring `#[openapi]` annotations on `struct`s and routes.
129+
* **`core/`**: The brain of the operation. Contains AST parsers (`ra_ap_syntax`), OpenAPI parsers, and code patching logic. It performs surgical updates on Rust source files.
130+
* **`cli/`**: The automation runner. Orchestrates `diesel`, `dsync`, and the core library to execute pipelines.
131+
* **`web/`**: The reference implementation. A standard Actix Web project that demonstrates how generated models and tests live alongside hand-written logic.
26132

27-
Key other advantages are:
133+
## 🎨 Design Principles
28134

29-
- automated updating of tests and docs, making it feasible to maintain 100% coverage without trading off development agility;
30-
- synchronisation across language boundaries (e.g., between the frontends, and from them to the backend).
135+
* **Non-Destructive:** We respect your existing code style, comments, and spacing.
136+
* **Type Safety:** `Uuid`, `jso::serde::Value`, and `chrono::DateTime` are first-class citizens.
137+
* **Dependencies:**
138+
* **Actix Web:** For the HTTP layer.
139+
* **Diesel:** For ORM/DB layer.
140+
* **Utoipa:** For OpenAPI attribute generation.
141+
* **Ra_ap_syntax:** For 100% accurate code manipulation.
31142

32-
Longer-term there are many other advantages, including:
143+
## 🔮 Roadmap
33144

34-
- inversion of control, enabling the business analyst to design schemas (Google Forms or even MS Access style);
35-
- simplifying separating projects out into multiple smaller projects, and smaller projects into a big project;
36-
- providing an alternative to NoSQL for many user-defined schema scenarios (such as a survey-builder site).
145+
- [x] Structural Diffing & AST Patching
146+
- [x] Database Model Synchronization
147+
- [x] Contract Test Generation
148+
- [x] Handler Scaffolding (Core Library Support)
149+
- [ ] Visual Schema Designer Integration
150+
- [ ] Expose Handler Scaffolding via CLI
37151

38152
---
39153

cli/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "cdd-cli"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
cdd-core = { path = "../core" }
8+
clap = { version = "4.4", features = ["derive"] }
9+
dsync = "0.1.0"
10+
walkdir = "2.5.0"
11+
12+
[dev-dependencies]
13+
tempfile = "3.8"

cli/src/error.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![deny(missing_docs)]
2+
3+
//! # CLI Errors
4+
//!
5+
//! Error types for the CLI crate.
6+
7+
use derive_more::{Display, Error, From};
8+
9+
/// Main error enum for CLI operations.
10+
#[derive(Debug, Display, From, Error)]
11+
pub enum CliError {
12+
/// IO Error wrapper.
13+
#[display(fmt = "IO Error: {}", _0)]
14+
Io(std::io::Error),
15+
16+
/// General failure message.
17+
#[display(fmt = "Operation failed: {}", _0)]
18+
General(String),
19+
}
20+
21+
/// Result type alias.
22+
pub type CliResult<T> = Result<T, CliError>;

0 commit comments

Comments
 (0)