[](https://crates.io/crates/ctgen)
[](https://docs.rs/ctgen)
[](https://deps.rs/repo/github/cytecbg/ctgen)
# About
Code generation tool meant to reduce repetitive tasks in day-to-day operations.
Generate code or text documents based on pre-defined code templates and a database schema.
Currently supports only MySQL/MariaDB databases with InnoDB table storage engine.
Code templates are written in [`handlebars`](https://handlebarsjs.com/guide/) format and support [`rhai`](https://rhai.rs/book/) scripts.
# Install
Run `cargo install ctgen` (if this project reached the public crate stage).
Or alternatively clone the repository and run `cargo build --release` and then
copy `target/release/ctgen` to a bin path of your choice.
Or just `cargo install --path .`
To see some hands-on examples, check [ctgen-samples](https://github.com/cytecbg/ctgen-samples).
# Usage
There are 3 modes of operation (commands).
1. The [`init`](#create-profile) command is for creating a new configuration profile project.
2. The [`config`](#manage-profiles) command is for managing existing configuration profiles.
3. The [`run`](#run-tasks) command is for running a generation task inside another project.
## Create profile
To create your first configuration profile go somewhere in your filesystem and run `ctgen init`.
Optionally you can create the profile project in a new directory by running `ctgen init <dirname>`.
To avoid being prompted for a profile name, use the `--name` option: `ctgen init --name backend backend_templates`.
This will create a new configuration profile project and register it using the same name.
The default project layout is:
- Profile config file: [`Ctgen.toml`](#profile-toml-schema). Describes the profile behavior, templates and build targets.
- Templates directory: `assets/templates`. Contains all `handlebars` templates with `.hbs` extension. The main part of the filename is the template name.
- Scripts directory: `assets/scripts`. Contains all `rhai` scripts with `.rhai` extension. The main part of the filename is used to register the script as handlebars helper.
## Manage profiles
- To add an existing configuration profile to the registry, run `ctgen config add [path to Ctgen.toml]`. If you are in the profile directory, you can just run `ctgen config add`. Optionally you can pass `--default` to override your default profile or `--name my_name` to override the profile name. Otherwise the name of the profile is used in the registry.
- To list registered profiles, run `ctgen config ls`. If an item in the list is blinking in red, that means that the profile is broken and the config file does not exist.
- To remove a profile from the registry, run `ctgen config rm profile_name`.
## Run tasks
Assuming you have a valid configuration profile setup already (see above), to run a generation task you need to:
- Go inside your project: `cd my_awesome_project`
- Run `ctgen run`
- Answer prompts
- ???
- PROFIT
Check `ctgen help run` for extra options like:
- Choosing a profile other than the `default` using `--profile=flutter`
- Overriding the profile setting for `.env` file, environment variable name, DSN string or target path
- Overriding the profile prompts with the `--prompt` option, for example `--prompt "dummy=1"`. Prompts answered with command-line params will be skipped during the run.
Example runs:
Let's imagine you are generating flutter code for your mobile project. Your profile is called `mobile`. It doesn't know where your database is so you enter it manually. You chose to generate code for table `clients` and know that the profile will ask you whether you want to generate a password reset flow and also add login with Google.
Run: `ctgen run --profile=mobile --dsn="mysql://root@127.0.0.1:3306/project_db" --prompt "password_reset=1" --prompt "google_auth=1" clients`
# Profile TOML Schema
The `Ctgen.toml` file describes the profile behavior and follows this set of rules:
1. The first section in the file is called `profile`, this section holds these fields:
- field `name`: the default profile name
- field `env-file`: the name of the env file to look for when trying to initialize context, typically `.env`
- field `env-var`: the name of the env variable to look for in the `.env` file, for example `DATABASE_CONNECTION`; the value of the variable is expected to be a valid DSN
- field `dsn`: if `env-file` and `env-var` are left empty, the profile could have a hardcoded database DSN instead; otherwise this field could be omitted or left blank
- field `target-dir`: this is the directory that should hold all build targets. It is relative to current working dir when running a generation task (`ctgen run`). CWD is used if left blank
- field `templates-dir`: this is the directory that holds all handlebars templates. It is relative to the profile containing directory.
- field `scripts-dir`: this is the directory that holds all rhai scripts. It is relative to the profile containing directory.
- field `prompts`: this is an array of strings. Every string in the array must be a valid prompt ID of a prompt defined in the `prompt` sections that follow.
- field `targets`: this is an array of strings. Every string in the array must be a valid target ID of a target defined in the `target` sections that follow.
2. Any number of `prompt` sections after the `profile` section declare profile prompts by assigning a prompt ID as a dot-nested value to the section name, for example `[prompt.dummy]`. A prompt can have the following fields (properties):
- field `condition`: optional, containing an inline handlebars template that should render `1` to trigger this prompt
- field `prompt`: containing plain text or an inline handlebars template that is being rendered to the user as prompt text
- field `options`: optional, containing either an array or table (object) of available options (for select and multiselect prompts), or string (for input prompts), or an inline handlebars template that renders a comma-separated list of options (for select and multi-select prompts)
- field `multiple`: optional, boolean flag indicating a multi-select; default is `false`
- field `ordered`: optional, boolean flag indicating that order matters for multi-select values; default is `false`
- field `required`: optional, boolean flag indicating that empty values will not be accepted; default is `false`
3. Any number of `target` sections after the `prompt` sections declare profile build targets by assigning a target ID as a dot-nested value to the section name, for example `[target.dummy]`. A target can have the following fields (properties):
- field `condition`: optional, containing an inline handlebars template that should render `1` to trigger this target to be rendered
- field `template`: string containing a template name, which should exist as a file with `.hbs` extension in the `templates-dir` directory. For example `dummy`, or `backend/dummy`.
- field `target`: string containing an inline handlebars template that should render to a file path inside the `target-dir`. Missing path elements will be created. Could also be plain text path like `main.rs`.
- field `formatter`: optional, containing an inline handlebars template that should render a valid shell command to execute after the target has been rendered and written to disk. Could also be plain text shell command if no context conditional parameters are necessary. NOTE: The only available variable to render is `{{target}}`.
# Notes
- If a rhai script file is named `op.rhai` inside `assets/scripts`, then you will have `{{op}}` helper available in your handlebars templates
- If your template file is named `backend.hbs` inside `assets/templates`, to define a target that uses that template, use the name `backend` as template name
- Available helpers (other than [handlebars](https://handlebarsjs.com/guide/builtin-helpers.html#if)' defaults) are: `{{inflect}}` [handlebars-inflector](https://crates.io/crates/handlebars-inflector), `{{concat}}` [handlebars-concat](https://crates.io/crates/handlebars-concat) and `{{json}}` (takes the first argument and turns it into a JSON)
- The context available during rendering handlebars templates looks roughly like:
```json
{
"database": {
"name": "db_name",
"tables": [],
"constraints": [],
"metadata": {}
},
"table_name": "selected_table_name",
"table": {
"name": "selected_table_name",
"primary_key": [],
"columns": [],
"indexes": [],
"metadata": {}
},
"constraints_local": [],
"constraints_foreign": [],
"prompts": {
"dummy": "1"
}
}
```
To dump your own context for debugging purposes use `{{{json this}}}` in your template.
# Acknowledgements
This tool relies heavily on [handlebars-rust](https://github.com/sunng87/handlebars-rust/) and [rhai](https://github.com/rhaiscript/rhai/) crates. :heart:
# TODO
- improve error handling
- improve logging
- improve `database-reflection` (add more adapters)
- improve `lib` layout and exports and isolate `cli` junk better
- more data sources, different context structs and context builders
- add dump/load context option to repeat runs
- more handlebars helpers
- maybe template updates by target reverse-engineering? based on diff?
- maybe fs watch to re-run task when templates change
- comments and documentation
- init produces crappy prompt options because of toml and serde circle of hell