Skip to content

Commit 0958151

Browse files
committed
feat: implement crud generation
1 parent 59e2e71 commit 0958151

19 files changed

Lines changed: 2629 additions & 138 deletions

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sqlx-gen"
3-
version = "0.3.0"
3+
version = "0.4.0"
44
edition = "2021"
55
description = "Generate Rust structs from database schema introspection"
66
license = "MIT"

README.md

Lines changed: 203 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# sqlx-gen
22

3-
Generate Rust structs from your database schema — with correct types, derives, and `sqlx::FromRow` annotations.
3+
Generate Rust structs and CRUD repositories from your database schema — with correct types, derives, and `sqlx` annotations.
44

55
Supports **PostgreSQL**, **MySQL**, and **SQLite**. Introspects tables, views, enums, composite types, and domains.
66

@@ -12,97 +12,215 @@ Supports **PostgreSQL**, **MySQL**, and **SQLite**. Introspects tables, views, e
1212

1313
- Multi-database: PostgreSQL, MySQL, SQLite
1414
- Multi-schema support (PostgreSQL)
15-
- Generates `#[derive(sqlx::FromRow)]` structs
15+
- Generates `#[derive(sqlx::FromRow)]` structs with `Serialize`, `Deserialize`, `PartialEq`, `Eq`
1616
- PostgreSQL enums → `#[derive(sqlx::Type)]` enums
1717
- PostgreSQL composite types and domains
1818
- MySQL inline ENUM detection
1919
- Correct nullable handling (`Option<T>`)
20-
- Custom derives (`--derives Serialize,Deserialize`)
20+
- Primary key detection across all backends
21+
- Custom derives (`--derives Hash`)
2122
- Type overrides (`--type-overrides jsonb=MyType`)
2223
- SQL views support (`--views`)
2324
- Table filtering (`--tables users,orders`) and exclusion (`--exclude-tables _migrations`)
2425
- Single-file or multi-file output
2526
- Dry-run mode (preview on stdout)
27+
- **CRUD repository generation** from generated entity files (no DB connection required)
28+
- `#[sqlx_gen(...)]` annotations on all generated types for tooling integration
29+
- Automatic `rustfmt` formatting (edition detected from `Cargo.toml`)
30+
- Automatic `mod.rs` management for generated CRUD files
2631

2732
## Installation
2833

2934
```sh
3035
cargo install sqlx-gen
3136
```
3237

33-
## Usage
38+
## Commands
39+
40+
sqlx-gen uses subcommands:
41+
42+
```
43+
sqlx-gen generate entities # Generate entity structs from DB schema
44+
sqlx-gen generate crud # Generate CRUD repository from an entity file
45+
```
46+
47+
## Generate Entities
3448

3549
### PostgreSQL (multi-schema)
3650
```sh
37-
sqlx-gen -u postgres://user:pass@localhost/mydb -s public,auth -o src/models
51+
sqlx-gen generate entities -u postgres://user:pass@localhost/mydb -s public,auth -o src/models
3852
```
3953

4054
### MySQL
4155
```sh
42-
sqlx-gen -u mysql://user:pass@localhost/mydb -o src/models
56+
sqlx-gen generate entities -u mysql://user:pass@localhost/mydb -o src/models
4357
```
4458

4559
### SQLite
4660
```sh
47-
sqlx-gen -u sqlite:./local.db -o src/models
61+
sqlx-gen generate entities -u sqlite:./local.db -o src/models
4862
```
4963

5064
### With extra derives
5165
```sh
52-
sqlx-gen -u postgres://... --derives Serialize,Deserialize -o src/models
66+
sqlx-gen generate entities -u postgres://... -D Hash -o src/models
5367
```
5468

5569
### Exclude specific tables
5670
```sh
57-
sqlx-gen -u postgres://... --exclude-tables _migrations,schema_versions -o src/models
71+
sqlx-gen generate entities -u postgres://... -x _migrations,schema_versions -o src/models
5872
```
5973

6074
### Include SQL views
6175
```sh
62-
sqlx-gen -u postgres://... --views -o src/models
76+
sqlx-gen generate entities -u postgres://... -v -o src/models
6377
```
6478

6579
### Dry run (preview without writing)
6680
```sh
67-
sqlx-gen -u postgres://... --dry-run
81+
sqlx-gen generate entities -u postgres://... -n
6882
```
6983

70-
## CLI Options
84+
### Entities CLI Options
7185

7286
| Flag | Short | Description | Default |
7387
|------|-------|-------------|---------|
74-
| `--database-url` | `-u` | Database connection URL (or `DATABASE_URL` env) | required |
75-
| `--output-dir` | `-o` | Output directory | `src/models` |
88+
| `--database-url` | `-u` | Database connection URL (or `DATABASE_URL` env var) | required |
7689
| `--schemas` | `-s` | Schemas to introspect (comma-separated) | `public` |
77-
| `--derives` | | Additional derive macros (comma-separated) | none |
78-
| `--type-overrides` | | Type overrides `sql_type=RustType` (comma-separated) | none |
79-
| `--tables` | | Only generate these tables (comma-separated) | all |
80-
| `--exclude-tables` | | Exclude these tables/views (comma-separated) | none |
81-
| `--views` | | Also generate structs for SQL views | false |
82-
| `--single-file` | | Write everything to a single `models.rs` | false |
83-
| `--dry-run` | | Print to stdout, don't write files | false |
90+
| `--output-dir` | `-o` | Output directory | `src/models` |
91+
| `--derives` | `-D` | Additional derive macros (comma-separated) | none |
92+
| `--type-overrides` | `-T` | Type overrides `sql_type=RustType` (comma-separated) | none |
93+
| `--single-file` | `-S` | Write everything to a single `models.rs` | `false` |
94+
| `--tables` | `-t` | Only generate these tables (comma-separated) | all |
95+
| `--exclude-tables` | `-x` | Exclude these tables/views (comma-separated) | none |
96+
| `--views` | `-v` | Also generate structs for SQL views | `false` |
97+
| `--dry-run` | `-n` | Print to stdout, don't write files | `false` |
98+
99+
## Generate CRUD
100+
101+
Generate a repository from an already-generated entity file. No database connection is required — the generator reads the Rust source file directly.
102+
103+
You must specify which methods to generate with `--methods` (`-m`):
104+
105+
```sh
106+
# Generate all CRUD methods
107+
sqlx-gen generate crud \
108+
-f src/models/users.rs \
109+
-d postgres \
110+
-m '*' \
111+
-o src/repositories
112+
113+
# Generate only specific methods
114+
sqlx-gen generate crud \
115+
-f src/models/users.rs \
116+
-d postgres \
117+
-m get_all,get,insert
118+
119+
# With explicit module path (auto-detected by default)
120+
sqlx-gen generate crud \
121+
-f src/models/users.rs \
122+
-d postgres \
123+
-e crate::models::users \
124+
-m '*'
125+
126+
# With compile-time checked macros
127+
sqlx-gen generate crud \
128+
-f src/models/users.rs \
129+
-d postgres \
130+
-m '*' \
131+
-q
132+
```
133+
134+
### Module path auto-detection
135+
136+
The `--entities-module` (`-e`) option is **optional**. When omitted, the module path is automatically derived from the `--entity-file` path by locating `src/` and converting to a Rust module path:
137+
138+
| File path | Derived module |
139+
|-----------|---------------|
140+
| `src/models/users.rs` | `crate::models::users` |
141+
| `src/db/entities/agent.rs` | `crate::db::entities::agent` |
142+
| `src/models/mod.rs` | `crate::models` |
143+
| `../project/src/models/users.rs` | `crate::models::users` |
144+
145+
### Views
146+
147+
Views are automatically detected via the `#[sqlx_gen(kind = "view")]` annotation — write methods (`insert`, `update`, `delete`) are never generated for views even if requested.
148+
149+
### Compile-time checked macros
150+
151+
By default, the CRUD generator uses `sqlx::query_as::<_, T>()` with `.bind()` chains (runtime). Pass `--query-macro` (`-q`) to generate `sqlx::query_as!()` / `sqlx::query!()` macros instead, which are checked at compile time (requires `DATABASE_URL` at build time).
152+
153+
### Available methods
154+
155+
| Method | Description |
156+
|--------|------------|
157+
| `*` | Generate all methods below |
158+
| `get_all` | `SELECT *` returning `Vec<T>` |
159+
| `paginate` | `SELECT *` with `LIMIT` / `OFFSET` returning `Vec<T>` |
160+
| `get` | `SELECT *` by primary key returning `Option<T>` |
161+
| `insert` | `INSERT` with a params struct, `RETURNING *` |
162+
| `update` | `UPDATE` by primary key with a params struct, `RETURNING *` |
163+
| `delete` | `DELETE` by primary key |
164+
165+
### mod.rs management
166+
167+
When writing a CRUD file (not in dry-run mode), sqlx-gen automatically updates or creates a `mod.rs` in the output directory with the corresponding `pub mod` declaration.
168+
169+
### Formatting
170+
171+
Generated files are automatically formatted with `rustfmt`. The Rust edition is detected from the nearest `Cargo.toml` in the output directory's parent chain (defaults to `2021` if not found).
172+
173+
### CRUD CLI Options
174+
175+
| Flag | Short | Description | Default |
176+
|------|-------|-------------|---------|
177+
| `--entity-file` | `-f` | Path to the generated entity `.rs` file | required |
178+
| `--db-kind` | `-d` | Database kind: `postgres`, `mysql`, `sqlite` | required |
179+
| `--entities-module` | `-e` | Rust module path (e.g. `crate::models::users`). Auto-detected from file path if omitted. | auto |
180+
| `--output-dir` | `-o` | Output directory | `src/crud` |
181+
| `--methods` | `-m` | Methods to generate (comma-separated): `*`, `get_all`, `paginate`, `get`, `insert`, `update`, `delete` | required |
182+
| `--query-macro` | `-q` | Use `sqlx::query_as!()` macros (compile-time checked) | `false` |
183+
| `--dry-run` | `-n` | Print to stdout, don't write files | `false` |
84184

85185
## Example Output
86186

187+
### Entity (table)
188+
87189
```rust
88190
// Auto-generated by sqlx-gen. Do not edit.
89191
// Table: public.users
90192

91193
use chrono::{DateTime, Utc};
194+
use serde::{Serialize, Deserialize};
92195
use uuid::Uuid;
93196

94-
#[derive(Debug, Clone, sqlx::FromRow)]
197+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
198+
#[sqlx_gen(kind = "table", table = "users")]
95199
pub struct Users {
200+
#[sqlx_gen(primary_key)]
96201
pub id: Uuid,
97202
pub email: String,
98203
pub name: Option<String>,
99204
pub created_at: DateTime<Utc>,
100205
}
101206
```
102207

103-
Enums:
208+
### Entity (view)
209+
210+
```rust
211+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
212+
#[sqlx_gen(kind = "view", table = "active_users")]
213+
pub struct ActiveUsers {
214+
pub id: Uuid,
215+
pub email: String,
216+
}
217+
```
218+
219+
### Enum
220+
104221
```rust
105-
#[derive(Debug, Clone, PartialEq, sqlx::Type)]
222+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
223+
#[sqlx_gen(kind = "enum")]
106224
#[sqlx(type_name = "status")]
107225
pub enum Status {
108226
#[sqlx(rename = "active")]
@@ -113,6 +231,67 @@ pub enum Status {
113231
}
114232
```
115233

234+
### CRUD Repository (default — runtime)
235+
236+
```rust
237+
impl UsersRepository {
238+
pub async fn get(&self, id: &Uuid) -> Result<Option<Users>, sqlx::Error> {
239+
sqlx::query_as::<_, Users>("SELECT * FROM users WHERE id = $1")
240+
.bind(id)
241+
.fetch_optional(&self.pool)
242+
.await
243+
}
244+
245+
pub async fn insert(&self, params: &InsertUsersParams) -> Result<Users, sqlx::Error> {
246+
sqlx::query_as::<_, Users>(
247+
"INSERT INTO users (email, name, created_at) VALUES ($1, $2, $3) RETURNING *",
248+
)
249+
.bind(&params.email)
250+
.bind(&params.name)
251+
.bind(&params.created_at)
252+
.fetch_one(&self.pool)
253+
.await
254+
}
255+
// ...
256+
}
257+
```
258+
259+
### CRUD Repository (`--query-macro` — compile-time checked)
260+
261+
```rust
262+
impl UsersRepository {
263+
pub async fn get(&self, id: &Uuid) -> Result<Option<Users>, sqlx::Error> {
264+
sqlx::query_as!(Users, "SELECT * FROM users WHERE id = $1", id)
265+
.fetch_optional(&self.pool)
266+
.await
267+
}
268+
269+
pub async fn insert(&self, params: &InsertUsersParams) -> Result<Users, sqlx::Error> {
270+
sqlx::query_as!(
271+
Users,
272+
"INSERT INTO users (email, name, created_at) VALUES ($1, $2, $3) RETURNING *",
273+
params.email, params.name, params.created_at
274+
)
275+
.fetch_one(&self.pool)
276+
.await
277+
}
278+
// ...
279+
}
280+
```
281+
282+
## Annotations
283+
284+
All generated types include `#[sqlx_gen(...)]` annotations for tooling:
285+
286+
| Type | Annotation |
287+
|------|-----------|
288+
| Table struct | `#[sqlx_gen(kind = "table", table = "name")]` |
289+
| View struct | `#[sqlx_gen(kind = "view", table = "name")]` |
290+
| Enum | `#[sqlx_gen(kind = "enum")]` |
291+
| Composite type | `#[sqlx_gen(kind = "composite")]` |
292+
| Domain type | `#[sqlx_gen(kind = "domain")]` |
293+
| Primary key field | `#[sqlx_gen(primary_key)]` |
294+
116295
## License
117296

118297
MIT

0 commit comments

Comments
 (0)