>_EXECUTIVE SUMMARY
api-rest-generator is a REST backend code generator. Input is a raw CREATE TABLE dump in any of four SQL dialects (MySQL, PostgreSQL, SQLite, MSSQL). Output is a complete REST surface — entities, controllers, DAOs / repositories, migrations, and module wiring — targeting one of four backends (Spring Boot in Java, Kotlin, or Groovy; or Loco in Rust with Axum + SeaORM). Two binaries ship from one crate: api-rest-generator (full multi-target dispatch) and loco-gen (focused Rust-only flag surface, same engine).
Status: mid-transition. The repo currently carries two implementations of the same generator — the original Kotlin/Gradle pipeline (9,094 LOC) and the Rust/Cargo port (3,956 LOC). Both compile and produce output independently. The Rust port is being grown module-for-module against the Kotlin reference, with a parity test (tests/parity_smoke.rs) that runs both pipelines over shared sample DDLs and diffs the output. When parity holds across the full sample corpus + every dialect × target combination, the Kotlin path retires; until then both ship.
~DIALECT × TARGET MATRIX
Every input dialect feeds every output target. The crate ships sample DDLs covering all four dialect families; the test suite parameterises across (dialect × target) so a regression in any one cell fails CI for both pipelines.
| Dialect ↓ / Target → | Java (Spring Boot) | Kotlin (Spring Boot) | Groovy (Spring Boot) | Rust/Loco (Axum + SeaORM) |
|---|---|---|---|---|
| MySQL | OK | OK | OK | OK |
| PostgreSQL | OK | OK | OK | OK |
| SQLite | OK | OK | OK | OK |
| MSSQL | OK | OK | OK | OK |
$SAMPLE DDL CORPUS
Real-world reference schemas covering every supported dialect, used as parity-test fixtures and as demo inputs. All four live in samples/.
| Sample | Dialect | Source | Tables | Lines |
|---|---|---|---|---|
chinook-sqlite.sql | SQLite | Chinook digital media store; [bracketed] identifiers + composite PKs | 11 | 15,902 |
northwind-mssql.sql | MSSQL | Classic MSSQL; double-quoted identifiers + Order Details space-in-name | 13 | 9,350 |
pagila-postgresql.sql | PostgreSQL | Postgres Sakila port; ALTER TABLE ADD PRIMARY KEY style | 22 | 1,842 |
sakila-mysql.sql | MySQL | DVD rental store; exercises triggers/views/functions filtering | 16 | 644 |
| Aggregate corpus | 27,738 | |||
Table counts are the README-reported figures for each schema; tests/sample_ddls.rs pins the parse with conservative lower bounds (Sakila ≥15, Chinook ≥11, Pagila ≥15, Northwind ≥10) plus invariants — every table gets a PK (synthetic where needed) and no quote/bracket/tab leaks into the parsed identifiers. The Loco toolchain is not required to run this test, so parser/normalizer regressions are caught in CI without installing loco.
%INTERFACE SURFACE
Two entry points with two operating models. api-rest-generator is config-driven (no CLI flags — it reads src/main/resources/config.properties at a hard-coded relative path); loco-gen is flag-driven with new / generate subcommands.
// config.properties (api-rest-generator + Kotlin pipeline)
| Key | Values | Default |
|---|---|---|
target.folder | any path | build/generated/src/main/<lang>/ |
target.package | com/example/generated | (empty) |
file.name | e.g. mysql_dump.sql | (empty) |
target.language | java / kotlin / groovy / rust-loco (alias loco) | java (unknown values fall through to java) |
database.type | mysql / postgresql / sqlite / mssql | mysql |
// loco-gen subcommands + flags (Rust/Loco)
| Flag | Default | Applies to |
|---|---|---|
--ddl FILE / -d | required | new + generate |
--name NAME / -n | required | new |
--out DIR / -o | . (new) / required (generate) | new + generate |
--db DIALECT | mysql | new + generate |
--loco-db DB | sqlite (or postgres) | new |
--wire | off | new + generate |
--migrate | off | new |
--loco-bin | loco | new |
loco-gen new shells out to loco new -n <name> --db <loco-db> --bg none --assets none, then writes entities / controllers / migrations into the scaffold. --wire rewrites an idempotent // BEGIN loco-gen routes block in src/app.rs (one .add_route(crate::controllers::<table>::routes()) per entity), preserving scaffold-shipped modules like controllers::home.
&KOTLIN → RUST PORT STATUS
The Rust port mirrors the Kotlin layout module-for-module to keep the diff readable. Each Kotlin source file has (or will have) a matching .rs port; the table below pins which ones are live.
| Kotlin source | Rust port | Status |
|---|---|---|
Main.kt | src/bin/main.rs + src/bin/loco_gen.rs | Ported — two binaries split out |
utils/Util.kt | src/parser.rs | Ported — DDL → Vec<Entity> |
dto/Entity.kt + dto/ColumnToField.kt | src/entity.rs | Ported — data structs |
utils/EntityToRESTConstants.kt | src/constants.rs | Ported — SQL-type → target-type maps for all 4 dialects |
utils/Configuration.kt | src/config.rs | Ported — config-file loading + per-language defaults |
utils/Globals.kt | src/globals.rs | Ported — mutable global state (shape preserved for transition) |
utils/Util.kt (name shaping) | src/normalize.rs | Ported — snake_case ↔ PascalCase / camelCase |
templates/Templates.kt | src/templates.rs | Ported — .tmpl loader + {{placeholder}} renderer (JVM targets) |
| — (Rust-native, no Kotlin equivalent) | src/loco.rs | New — Loco/SeaORM codegen path; no JVM analogue |
| — | src/lib.rs | New — public API surface for programmatic use |
Rust source totals 3,956 LOC across 11 files; the Kotlin codebase totals 9,094 LOC across 36 .kt files (8 main + 28 test). The 2.3× line-count ratio reflects Kotlin's reliance on Spring / JPA / Lombok annotations (which expand into JVM bytecode the Rust port doesn't need an annotation framework to produce) and Java-style class boilerplate the Rust port collapses into structs + free functions.
@DIALECT NORMALISATION
Each non-MySQL dialect runs a dedicated pre-parse normalizer (src/normalize.rs) so the shared parse_words cascade sees a clean token stream. MySQL needs none — its CREATE TABLE grammar parses directly.
| Dialect | Normalizer | What it does |
|---|---|---|
| MySQL | — (none) | Backticked identifiers + inline/table-level constraints parse directly. |
| PostgreSQL | normalize_postgresql_words | Handles pg_dump shape: post-table ALTER TABLE ... ADD CONSTRAINT for PK/FK, PG-native type tokens. |
| SQLite | normalize_sqlite_words | Strips IF NOT EXISTS and MSSQL-style [brackets]; drops entire PRAGMA / INSERT / BEGIN / COMMIT / DELETE runs up to the next CREATE / ALTER. |
| MSSQL | normalize_mssql_words | Collapses quoted multi-word identifiers ("Order Details"), strips [...] and "..." escapes, folds (max) out of types, and drops SET / USE / GO / EXEC / PRINT / IF / DROP / INSERT / BEGIN / END control statements. |
^RUST/LOCO COLTYPE MAPPING
The Loco emitter (src/loco.rs) maps SQL tokens to SeaORM Rust types and the matching Loco ColType for the create_table migration.
| SQL type | Rust (SeaORM) | Loco ColType |
|---|---|---|
bigint, int8, bigserial | i64 | BigInteger |
int, integer, int4, serial | i32 | Integer |
smallint, int2 | i16 | SmallInteger |
varchar, char, text, nvarchar | String | String |
bool, boolean, bit | bool | Boolean |
float, real | f32 | Float |
double, decimal, numeric, money | f64 | Double |
date / time | Date / Time | Date / Time |
datetime, datetime2, timestamp(tz) | DateTimeWithTimeZone | TimestampWithTimeZone |
blob, bytea, binary, varbinary, image | Vec<u8> | Blob |
| primary key column (any) | i32 | PkAuto |
Reserved Rust keywords (a column named type / match / move) are emitted as raw identifiers (r#type) so the structs compile without losing the original column name in JSON. tests/loco_keyword_escape.rs pins this; tests/loco_collision_pins.rs pins snake-table collisions, duplicate emission, FK-clobber, and silent-drop edge cases in write_loco_project.
!TEST COVERAGE
Both pipelines carry their own test suite; the Rust suite is grown alongside the port to lock in parity as each module lands.
| Suite | Lives in | Count | Scope |
|---|---|---|---|
Rust unit (#[test] in src/) | src/*.rs | 155 | Per-module: parser, normalize, constants, templates, loco, entity, config |
Rust integration (tests/) | tests/*.rs | 63 | End-to-end on sample DDLs, parity vs Kotlin, normalize invariants, Loco smoke / collisions / edge cases / keyword escape |
| Rust total | — | 218 | — |
| Kotlin @Test | src/test/kotlin/... | 806 | Original Kotlin pipeline: unit + template + integration (per README test table: 28+ classes covering parser, type mappers, template engine, full pipelines, cross-DB × cross-language matrix) |
// RUST INTEGRATION TESTS
| File | What it pins |
|---|---|
tests/sample_ddls.rs | All 4 sample DDLs (chinook / northwind / pagila / sakila) round-trip cleanly through the Rust parser → codegen path for every target. |
tests/parity_smoke.rs | Runs both pipelines (Kotlin via ./gradlew run, Rust via cargo run) on the shared fixture set and diffs the output. The transition is "done" when this passes empty across the full sample corpus. |
tests/normalize_units.rs | Property-style coverage of the snake_case ↔ PascalCase / camelCase transforms — the single hottest source of cross-language drift. |
tests/loco_smoke.rs | The Rust-native target (loco-gen) end-to-end — no Kotlin analogue, so parity testing doesn't apply. |
tests/loco_collision_pins.rs | Loco codegen: snake-table collisions, duplicate emission, FK-clobber, and silent-drop edge cases in write_loco_project. |
tests/loco_edge_cases.rs | Loco codegen edge cases on malformed / boundary DDL inputs. |
tests/loco_keyword_escape.rs | Rust-keyword escaping (self / Self / crate / super / r#type) in generated SeaORM field names. |
// KOTLIN TEST CATEGORIES
Full per-class breakdown lives in README “RUNNING TESTS”. Three tiers:
- Unit —
MainTest,UtilTest,KotlinUtilTest,GroovyUtilTest,ColumnToFieldTest,EntityTest,EntityToRESTConstantsTest,EdgeCaseParsingTest,ConfigurationTest,KotlinConfigurationTest,GroovyConfigurationTest: pinpoint per-fn coverage of parsers, type mappers, name transforms. - Template —
TemplatesTest,KotlinTemplatesTest,GroovyTemplatesTest,KotlinEntityFieldDefaultsTest,GroovyEntityFieldsTest,RestRepositoryTemplateTest: pin per-target template emission shape. - Integration —
FullPipelineTest,PostgresqlPipelineTest,SqlitePipelineTest,MssqlPipelineTest,KotlinPipelineTest,GroovyPipelineTest,CustomSqlParsingTest,EmptyEntityListTest,KotlinContentValidationTest,GroovyContentValidationTest,CrossDatabaseLanguageTest: end-to-end pipelines across the dialect × language matrix.
#CI POSTURE
api-rest-generator is in the meta-repo's polish-gates allowlist with all four gates opt-out for the Rust path: fmt, clippy, doc, test. The allowlist entry reads “mid-transition from Kotlin to Rust — Rust code is still the secondary path. Treat as opt-out until the transition completes.” The Kotlin path stays the source of truth; Rust ports land continuously without blocking on Rust-side polish until parity is established.
/PROJECT METADATA
| Item | Value |
|---|---|
| License | MIT |
| Author | MenkeTechnologies |
| Repository | github.com/MenkeTechnologies/api-rest-generator |
| Crate | crates.io/crates/api-rest-generator |
| API docs | docs.rs/api-rest-generator |
| Issues | github.com/MenkeTechnologies/api-rest-generator/issues |
| Edition | Rust 2021 (Cargo) · Kotlin/JVM (Gradle) |