|
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) |
| 1 | +@AGENTS.md |
0 commit comments