Skip to content

Commit 96125e3

Browse files
committed
Update docs
1 parent b0292b0 commit 96125e3

File tree

3 files changed

+262
-174
lines changed

3 files changed

+262
-174
lines changed

CLAUDE.md

Lines changed: 172 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,172 @@
1-
# CLAUDE.md
2-
3-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4-
5-
## Overview
6-
7-
Vespertide is a Rust workspace for defining database schemas in JSON/YAML and generating migration plans and SQL from model diffs. It enables declarative schema management by comparing the current model state against a baseline reconstructed from applied migrations.
8-
9-
## Build and Test Commands
10-
11-
```bash
12-
# Build the entire workspace
13-
cargo build
14-
15-
# Run all tests
16-
cargo test
17-
18-
# Run tests for a specific crate
19-
cargo test -p vespertide-core
20-
cargo test -p vespertide-planner
21-
22-
# Format code
23-
cargo fmt
24-
25-
# Lint (important: use all targets and features)
26-
cargo clippy --all-targets --all-features
27-
28-
# Regenerate JSON schemas
29-
cargo run -p vespertide-schema-gen -- --out schemas
30-
31-
# Run CLI commands (use -p vespertide-cli)
32-
cargo run -p vespertide-cli -- init
33-
cargo run -p vespertide-cli -- new user
34-
cargo run -p vespertide-cli -- diff
35-
cargo run -p vespertide-cli -- sql
36-
cargo run -p vespertide-cli -- revision -m "message"
37-
cargo run -p vespertide-cli -- status
38-
cargo run -p vespertide-cli -- log
39-
```
40-
41-
## Architecture
42-
43-
### Core Data Flow
44-
45-
1. **Schema Definition**: Users define tables in JSON files (`TableDef`) in the `models/` directory
46-
2. **Baseline Reconstruction**: Applied migrations are replayed to rebuild the baseline schema
47-
3. **Diffing**: Current models are compared against the baseline to compute changes
48-
4. **Planning**: Changes are converted into a `MigrationPlan` with versioned actions
49-
5. **SQL Generation**: Migration actions are translated into SQL statements
50-
51-
### Crate Responsibilities
52-
53-
- **vespertide-core**: Data structures (`TableDef`, `ColumnDef`, `ColumnType`, `MigrationAction`, `MigrationPlan`, constraints, indexes)
54-
- `ColumnType` enum with `Simple(SimpleColumnType)` and `Complex(ComplexColumnType)` variants
55-
- `ColumnType::to_sql()` and `ColumnType::to_rust_type()` methods for type conversion
56-
- **vespertide-planner**:
57-
- `schema_from_plans()`: Replays applied migrations to reconstruct baseline schema
58-
- `diff_schemas()`: Compares two schemas and generates migration actions
59-
- `plan_next_migration()`: Combines baseline reconstruction + diffing to create the next migration
60-
- `apply_action()`: Applies a single migration action to a schema (used during replay)
61-
- `validate_*()`: Validates schemas and migration plans
62-
- **vespertide-query**: Converts `MigrationAction` → SQL with bind parameters
63-
- Uses `ColumnType::to_sql()` method for SQL type conversion
64-
- **vespertide-config**: Manages `vespertide.json` (models/migrations directories, naming case preferences)
65-
- **vespertide-cli**: Command-line interface implementation
66-
- **vespertide-exporter**: Exports schemas to other formats (e.g., SeaORM entities)
67-
- Uses `ColumnType::to_rust_type(nullable)` method for Rust type conversion
68-
- **vespertide-schema-gen**: Generates JSON Schema files for validation
69-
- **vespertide-macro**: Placeholder for future runtime migration executor
70-
71-
### Key Architectural Patterns
72-
73-
**Migration Replay Pattern**: The planner doesn't store a "current database state" - it reconstructs it by replaying all applied migrations in order. This ensures the baseline is always derivable from the migration history.
74-
75-
**Declarative Diffing**: Users declare the desired end state in model files. The diff engine compares this against the reconstructed baseline to compute necessary changes.
76-
77-
**Action-Based Migrations**: All changes are expressed as typed `MigrationAction` enums (CreateTable, AddColumn, ModifyColumnType, etc.) rather than raw SQL. SQL generation happens in a separate layer.
78-
79-
## Important Implementation Details
80-
81-
### ColumnDef Structure
82-
When creating `ColumnDef` instances in tests or code, you must initialize ALL fields including the newer inline constraint fields:
83-
84-
```rust
85-
ColumnDef {
86-
name: "id".into(),
87-
r#type: ColumnType::Simple(SimpleColumnType::Integer),
88-
nullable: false,
89-
default: None,
90-
comment: None,
91-
primary_key: None, // Inline PK declaration
92-
unique: None, // Inline unique constraint
93-
index: None, // Inline index creation
94-
foreign_key: None, // Inline FK definition
95-
}
96-
```
97-
98-
These inline fields (added recently) allow constraints to be defined directly on columns in addition to table-level `TableConstraint` definitions.
99-
100-
### ColumnType Structure
101-
`ColumnType` is an enum with two variants:
102-
- `Simple(SimpleColumnType)`: Built-in types like `Integer`, `Text`, `Boolean`, etc.
103-
- `Complex(ComplexColumnType)`: Types with parameters like `Varchar { length }` or `Custom { custom_type }`
104-
105-
**Important**: In Rust code, always use `ColumnType::Simple(SimpleColumnType::Integer)` instead of the old `ColumnType::Integer` syntax. The `From` trait is implemented for convenience:
106-
```rust
107-
// These are equivalent:
108-
ColumnType::Simple(SimpleColumnType::Integer)
109-
SimpleColumnType::Integer.into()
110-
```
111-
112-
### ColumnType Methods
113-
`ColumnType` provides two utility methods:
114-
- `to_sql()`: Returns the SQL type string (e.g., `"INTEGER"`, `"VARCHAR(255)"`)
115-
- `to_rust_type(nullable: bool)`: Returns the Rust type string for SeaORM entity generation (e.g., `"i32"` or `"Option<i32>"`)
116-
117-
These methods replace the old standalone functions `column_type_sql()` and `rust_type()`.
118-
119-
### Foreign Key Definition
120-
Foreign keys can be defined inline on columns via the `foreign_key` field:
121-
122-
```rust
123-
pub struct ForeignKeyDef {
124-
pub ref_table: TableName,
125-
pub ref_columns: Vec<ColumnName>,
126-
pub on_delete: Option<ReferenceAction>,
127-
pub on_update: Option<ReferenceAction>,
128-
}
129-
```
130-
131-
### Migration Plan Validation
132-
- Non-nullable columns added to existing tables require either a `default` value or a `fill_with` backfill expression
133-
- Schemas are validated for constraint consistency before diffing
134-
- The planner validates that column/table names follow the configured naming case
135-
136-
### SQL Generation Target
137-
SQL generation currently uses PostgreSQL-compatible syntax. The query builder can be extended to support other database systems.
138-
139-
### JSON Schema Generation
140-
The `vespertide-schema-gen` crate uses `schemars` to generate JSON Schemas from the Rust types. After modifying core data structures, regenerate schemas with:
141-
```bash
142-
cargo run -p vespertide-schema-gen -- --out schemas
143-
```
144-
145-
Schema base URL can be overridden via `VESP_SCHEMA_BASE_URL` environment variable.
146-
147-
## Testing Patterns
148-
149-
- Tests use helper functions like `col()` and `table()` to reduce boilerplate
150-
- Use `rstest` for parameterized tests (common in planner/query crates)
151-
- Use `serial_test::serial` for tests that modify the filesystem or working directory
152-
- Snapshot testing with `insta` is used in the exporter crate
153-
154-
## Limitations
155-
156-
- YAML loading is not implemented (templates can be generated but not parsed)
157-
- Runtime migration executor (`run_migrations`) in `vespertide-macro` is not implemented
158-
- SQL generation currently uses PostgreSQL-compatible syntax
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Overview
6+
7+
Vespertide is a Rust workspace for defining database schemas in JSON/YAML and generating migration plans and SQL from model diffs. It enables declarative schema management by comparing the current model state against a baseline reconstructed from applied migrations.
8+
9+
The workspace uses Rust **edition 2024**.
10+
11+
## Build and Test Commands
12+
13+
```bash
14+
# Build the entire workspace
15+
cargo build
16+
17+
# Run all tests
18+
cargo test
19+
20+
# Run tests for a specific crate
21+
cargo test -p vespertide-core
22+
cargo test -p vespertide-planner
23+
24+
# Run a single test
25+
cargo test -p vespertide-planner test_name
26+
27+
# Run tests with output shown
28+
cargo test -- --nocapture
29+
30+
# Format code
31+
cargo fmt
32+
33+
# Lint (important: use all targets and features)
34+
cargo clippy --all-targets --all-features
35+
36+
# Regenerate JSON schemas
37+
cargo run -p vespertide-schema-gen -- --out schemas
38+
39+
# Snapshot testing (exporter crate)
40+
cargo insta test -p vespertide-exporter
41+
cargo insta accept
42+
43+
# Run CLI commands (use -p vespertide-cli)
44+
cargo run -p vespertide-cli -- init
45+
cargo run -p vespertide-cli -- new user
46+
cargo run -p vespertide-cli -- diff
47+
cargo run -p vespertide-cli -- sql
48+
cargo run -p vespertide-cli -- revision -m "message"
49+
cargo run -p vespertide-cli -- status
50+
cargo run -p vespertide-cli -- log
51+
cargo run -p vespertide-cli -- export --orm seaorm
52+
```
53+
54+
## Architecture
55+
56+
### Core Data Flow
57+
58+
1. **Schema Definition**: Users define tables in JSON files (`TableDef`) in the `models/` directory
59+
2. **Baseline Reconstruction**: Applied migrations are replayed to rebuild the baseline schema
60+
3. **Diffing**: Current models are compared against the baseline to compute changes
61+
4. **Planning**: Changes are converted into a `MigrationPlan` with versioned actions
62+
5. **SQL Generation**: Migration actions are translated into SQL statements
63+
64+
### Crate Responsibilities
65+
66+
- **vespertide-core**: Data structures (`TableDef`, `ColumnDef`, `ColumnType`, `MigrationAction`, `MigrationPlan`, constraints, indexes)
67+
- `ColumnType` enum with `Simple(SimpleColumnType)` and `Complex(ComplexColumnType)` variants
68+
- `ColumnType::to_sql()` and `ColumnType::to_rust_type()` methods for type conversion
69+
- **vespertide-planner**:
70+
- `schema_from_plans()`: Replays applied migrations to reconstruct baseline schema
71+
- `diff_schemas()`: Compares two schemas and generates migration actions
72+
- `plan_next_migration()`: Combines baseline reconstruction + diffing to create the next migration
73+
- `apply_action()`: Applies a single migration action to a schema (used during replay)
74+
- `validate_*()`: Validates schemas and migration plans
75+
- **vespertide-query**: Converts `MigrationAction` → SQL with bind parameters
76+
- Uses `ColumnType::to_sql()` method for SQL type conversion
77+
- **vespertide-config**: Manages `vespertide.json` (models/migrations directories, naming case preferences)
78+
- **vespertide-cli**: Command-line interface implementation
79+
- **vespertide-exporter**: Exports schemas to other formats (e.g., SeaORM entities)
80+
- Uses `ColumnType::to_rust_type(nullable)` method for Rust type conversion
81+
- **vespertide-schema-gen**: Generates JSON Schema files for validation
82+
- **vespertide-loader**: Loads migrations and models from filesystem (JSON/YAML)
83+
- **vespertide-naming**: Naming conventions and case conversion helpers
84+
- **vespertide-macro**: Compile-time migration macro (`vespertide_migration!`) for SeaORM
85+
- **vespertide**: Main crate that re-exports vespertide-core and vespertide-macro
86+
87+
### Key Architectural Patterns
88+
89+
**Migration Replay Pattern**: The planner doesn't store a "current database state" - it reconstructs it by replaying all applied migrations in order. This ensures the baseline is always derivable from the migration history.
90+
91+
**Declarative Diffing**: Users declare the desired end state in model files. The diff engine compares this against the reconstructed baseline to compute necessary changes.
92+
93+
**Action-Based Migrations**: All changes are expressed as typed `MigrationAction` enums (CreateTable, AddColumn, ModifyColumnType, etc.) rather than raw SQL. SQL generation happens in a separate layer.
94+
95+
## Important Implementation Details
96+
97+
### ColumnDef Structure
98+
When creating `ColumnDef` instances in tests or code, you must initialize ALL fields including the newer inline constraint fields:
99+
100+
```rust
101+
ColumnDef {
102+
name: "id".into(),
103+
r#type: ColumnType::Simple(SimpleColumnType::Integer),
104+
nullable: false,
105+
default: None,
106+
comment: None,
107+
primary_key: None, // Inline PK declaration
108+
unique: None, // Inline unique constraint
109+
index: None, // Inline index creation
110+
foreign_key: None, // Inline FK definition
111+
}
112+
```
113+
114+
These inline fields (added recently) allow constraints to be defined directly on columns in addition to table-level `TableConstraint` definitions.
115+
116+
### ColumnType Structure
117+
`ColumnType` is an enum with two variants:
118+
- `Simple(SimpleColumnType)`: Built-in types like `Integer`, `Text`, `Boolean`, etc.
119+
- `Complex(ComplexColumnType)`: Types with parameters like `Varchar { length }` or `Custom { custom_type }`
120+
121+
**Important**: In Rust code, always use `ColumnType::Simple(SimpleColumnType::Integer)` instead of the old `ColumnType::Integer` syntax. The `From` trait is implemented for convenience:
122+
```rust
123+
// These are equivalent:
124+
ColumnType::Simple(SimpleColumnType::Integer)
125+
SimpleColumnType::Integer.into()
126+
```
127+
128+
### ColumnType Methods
129+
`ColumnType` provides two utility methods:
130+
- `to_sql()`: Returns the SQL type string (e.g., `"INTEGER"`, `"VARCHAR(255)"`)
131+
- `to_rust_type(nullable: bool)`: Returns the Rust type string for SeaORM entity generation (e.g., `"i32"` or `"Option<i32>"`)
132+
133+
These methods replace the old standalone functions `column_type_sql()` and `rust_type()`.
134+
135+
### Foreign Key Definition
136+
Foreign keys can be defined inline on columns via the `foreign_key` field:
137+
138+
```rust
139+
pub struct ForeignKeyDef {
140+
pub ref_table: TableName,
141+
pub ref_columns: Vec<ColumnName>,
142+
pub on_delete: Option<ReferenceAction>,
143+
pub on_update: Option<ReferenceAction>,
144+
}
145+
```
146+
147+
### Migration Plan Validation
148+
- Non-nullable columns added to existing tables require either a `default` value or a `fill_with` backfill expression
149+
- Schemas are validated for constraint consistency before diffing
150+
- The planner validates that column/table names follow the configured naming case
151+
152+
### SQL Generation Target
153+
SQL generation currently uses PostgreSQL-compatible syntax. The query builder can be extended to support other database systems.
154+
155+
### JSON Schema Generation
156+
The `vespertide-schema-gen` crate uses `schemars` to generate JSON Schemas from the Rust types. After modifying core data structures, regenerate schemas with:
157+
```bash
158+
cargo run -p vespertide-schema-gen -- --out schemas
159+
```
160+
161+
Schema base URL can be overridden via `VESP_SCHEMA_BASE_URL` environment variable.
162+
163+
## Testing Patterns
164+
165+
- Tests use helper functions like `col()` and `table()` to reduce boilerplate
166+
- Use `rstest` for parameterized tests (common in planner/query crates)
167+
- Use `serial_test::serial` for tests that modify the filesystem or working directory
168+
- Snapshot testing with `insta` is used in the exporter crate
169+
170+
## Limitations
171+
172+
- YAML loading is not implemented (templates can be generated but not parsed)

0 commit comments

Comments
 (0)