6 releases
0.1.5 | Aug 2, 2024 |
---|---|
0.1.4 | Apr 17, 2024 |
0.1.3 | Mar 18, 2024 |
0.1.1 | Feb 25, 2024 |
#188 in Template engine
87KB
1.5K
SLoC
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
format and support rhai
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.
Usage
There are 3 modes of operation (commands).
- The
init
command is for creating a new configuration profile project. - The
config
command is for managing existing configuration profiles. - The
run
command is for running a generation task inside another project.
Disclaimer
Under no circumstances should you ever run generation tasks based on templates you are not very well familiar with! This poses a great security threat!
When using ctgen
with templates that you did not create yourself you should read and study the code carefully before running any tasks with
that template! Ctgen can and does modify your local filesystem and has the capability to execute additional shell commands with or without user input!
Under no circumstances should you ever run ctgen
as root
or any other privileged account! As of time of writing ctgen
does NOT have any mechanisms to
predict or prevent any potentially negative outcomes or dangerous operations. Use discretion and study the templates you use before attempting to run any tasks!
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
. Describes the profile behavior, templates and build targets. - Templates directory:
assets/templates
. Contains allhandlebars
templates with.hbs
extension. The main part of the filename is the template name. - Scripts directory:
assets/scripts
. Contains allrhai
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 runctgen 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:
- 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 exampleDATABASE_CONNECTION
; the value of the variable is expected to be a valid DSN - field
dsn
: ifenv-file
andenv-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 theprompt
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 thetarget
sections that follow.
- Any number of
prompt
sections after theprofile
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 render1
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 isfalse
- field
ordered
: optional, boolean flag indicating that order matters for multi-select values; default isfalse
- field
required
: optional, boolean flag indicating that empty values will not be accepted; default isfalse
- Any number of
target
sections after theprompt
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 render1
to trigger this target to be rendered - field
template
: string containing a template name, which should exist as a file with.hbs
extension in thetemplates-dir
directory. For exampledummy
, orbackend/dummy
. - field
target
: string containing an inline handlebars template that should render to a file path inside thetarget-dir
. Missing path elements will be created. Could also be plain text path likemain.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
insideassets/scripts
, then you will have{{op}}
helper available in your handlebars templates - Rhai comes with the following additional packages enabled: rhai-chrono, rhai-sci, rhai-fs, rhai-url.
- If your template file is named
backend.hbs
insideassets/templates
, to define a target that uses that template, use the namebackend
as template name - Available helpers (other than handlebars' defaults) are:
{{inflect}}
handlebars-inflector,{{concat}}
handlebars-concat,{{datetime}}
handlebars-chrono and{{{json}}}
(takes the first argument and turns it into a JSON) - The context available during rendering handlebars templates looks roughly like:
{
"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"
},
"timestamp": "2024-03-18T21:35:09.750752900+00:00",
"ctgen_ver": "0.1.2"
}
To dump your own context for debugging purposes use {{{json this}}}
in your template.
Acknowledgements
This tool relies heavily on handlebars-rust and rhai crates. ❤️
TODO
- improve error handling
- improve logging
- improve
database-reflection
(add more adapters) - improve
lib
layout and exports and isolatecli
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
Dependencies
~32–46MB
~741K SLoC