This document describes the functions provided by the cargo-hatch binary as well as details about the templating engine and how to query the user for input.
The cargo-hatch
binary is the main way of generating new projects from templates in various ways.
Running cargo hatch help
should give a good overview of the possible commands and should be mostly
self-explanatory.
Additionally, all possible commands are explained in the following sub-sections as well.
Currently not implemented yet!
Initialize a new template by first invoking cargo init
/cargo new
to generate the default project
structure and then adding default .hatch.toml
and .hatchignore
files with comments that explain
how to configure the template.
Possible arguments are:
name
(optional): The name of the template which at the same time defines the folder it is generated in. It can be omitted to use the current folder as target and the project name is derived from the folder name.
List all known bookmarks to be used in cargo hatch new
, with the respective name and description.
Create a new project from a template defined in the global settings.
Possible arguments are:
bookmark
: Name of the bookmarked template to use.name
(optional): Name of the new project and target folder where it is generated in. It can be omitted to use the current folder as target, deriving the project name from the folder name.
Create a new project from a template located in a remote Git repository.
Possible arguments are:
url
: HTTP or Git URL of the repository.name
(optional): Name of the new project and target folder where it is generated in. It can be omitted to use the current folder as target, deriving the project name from the folder name.
Possible options are:
folder
(optional): Sub-folder within the repository that contains the template. Helpful if a single repository contains multiple templates.
Create a new project from a template located in the local file system.
Possible arguments are:
path
: Local path to the template.name
(optional): Name of the new project and target folder where it is generated in. It can be omitted to use the current folder as target, deriving the project name from the folder name.
Generate shell completions for cargo-hatch. Content is written to the standard output and won't write to your shell config files directly. The output can be redirected to a file by the user.
Possible arguments are:
shell
: The shell to generate completions for. Can be one ofzsh
,bash
,fish
,powershell
orelvish
.
For example, to configure completions for the fish
shell run the following:
cargo hatch completions fish > ~/.config/fish/completions/cargo-hatch.fish
Cargo hatch has a global config file that allows to further adjust it to your needs on a device level. That means it contains settings not specific to a template but to your usage of the binary.
Most of the git configuration is taken from your default git settings file on your device. Some extra settings are available under this category.
ssh_key
: The SSH key to use for authentication, defined as a file system path. If the path is relative, it's considered relative to your home folder.- Cargo hatch will default to use your
ssh-agent
to get the right authentication key, if this setting is omitted.
- Cargo hatch will default to use your
For example, using an Ed25519-based SSH key:
[git]
ssh_key = ".ssh/id_ed25519"
Similar to how your browser uses bookmarks to allow for shortcuts to often used sites, cargo hatch allows to save shortcuts to commonly used templates.
Each entry is defined with a [bookmarks.<name>]
key where the <name>
defines the name of it. The
mandatory fields are:
repository
: Git location of the template. Must be a HTTP or Git URL.- HTTP URL example:
https://github.com/dnaka91/awesome-template.git
. - Git URL example:
git@github.com:dnaka91/awesome-template.git
.
- HTTP URL example:
Additionally, the optional values fields are:
description
: Short description of the template that is shown when listing known bookmarks.folder
: A sub-folder within the repository for use with mono-repos that contain multiple templates. If this is set, only the sub-folder is used as template root, ignoring the rest of the repository.defaults
: Overrides for the template's own defaults. This allows to set differnt pre-selected values for the arguments of the template. In addition, it allows to skip the prompt altogether, and use the default value as input.
For example:
[bookmarks.server]
repository = "git@github.com:dnaka91/rust-server-template.git"
description = "Basic template for a web-server using the `axum` crate"
The above mentioned defaults
fields allows to override a template's default selections for
arguments, and skip them altogether as well. The setting is a mapping from argument name to its
settings, and best configured as a separate table:
[bookmarks.server]
# Same as above here, omitted for simplicity
# Assuming there is a boolean prompt named `with_tracing` in the original
# template, that we want to override with `true` and completely skip.
[bookmarks.server.defaults]
with_tracing = { value = { bool = true }, skip_prompt = true }
Each override requires exactly two fields:
value
: The default value to use instead. It must match to the type of argument from the template settins:- bool:
value = { bool = true }
- string:
value = { string = "value" }
- number:
value = { number = 10 }
- float:
value = { float = 2.5 }
- list:
value = { list = "one" }
- multi_list:
value = { multi_list = ["one", "two"] }
- bool:
skip_prompt
: Whether to skip the prompt and take the default value as input.
The .hatch.toml
file is the main configuration point of a template and required as a marker
for the repository (even if it's empty). The file itself is excluded from the generated files.
The project name is taken from new
/git
/local
commands, either provided on the CLI or derived
from the current directory. It is globally available in each template through the project_name
variable.
Key: crate_type
The crate type is either bin
or lib
, exactly as with cargo init
/cargo new
. By default the
type is queried from the user during execution but depending on the template it makes sense to fix
the type instead. If this value is set it wont be asked during execution.
For example a template for web servers is likely always a bin
crate:
crate_type = "bin"
Based on this value, 3 pre-defined values are inserted into every templates context:
crate_type
(string): Eitherbin
orlib
, same as the setting.crate_bin
(bool):true
if the crate type isbin
,false
otherwise.crate_lib
(bool):true
if the crate type islib
,false
otherwise.
For example, the following template:
Crate type: {{ crate_type }}
Is bin: {{ crate_bin }}
Is lib: {{ crate_lib }}
Would render to the following, given that bin
was selected for the crate_type
:
Crate type: bin
Is bin: true
Is lib: false
Additional git information is loaded from the device's Git configuration files and put into the context of each template. The provided values are:
git_author
: The Git user name and email combined as it was used bycargo
until it was marked deprecated. The format isname <email>
.git_name
: User name as defined in the git config'suser.name
field.git_email
: User email as defined in the git config'suser.email
field.
Key: ignore
Sometimes you want to ignore certain files and folders based on the arguments selected by the user.
For example some files may only be needed for a library or binary crate. You can do so by using the
[[ignore]]
settings, which allow you to use the available template variables to decide upon file
exclusion.
You can create as many ignore settings as you like. Each requires a list of glob patterns to select the files to exclude, and a condition to decide whether to actually exclude these files.
paths
: List of glob patterns to select excluded files.condition
: Tera template that is executed to determine exclusion. It is filled with all the pre-defined and user-selected variables, same as the regular templates. It must evaluate to eithertrue
orfalse
.
The following is a basic example, that excludes the src/main.rs
when creating a library crate, and
excludes CLI related Rust files and shell completions when the selected kind
is a web server.
[[ignore]]
paths = ["src/main.rs"]
condition = "{{ crate_lib }}"
[[ignore]]
paths = ["src/cli/**", "completions/**"]
condition = '{{ kind == "webserver" }}'
Arguments are extra values that are queried from the user to render the template and specific to each individual template project. As the bare minimum, each argument must provide the following settings:
type
: To define what kind of argument is used. One ofbool
,string
,number
,float
,list
ormulti_list
.description
: Short description of the value being asked for and printed out to the user when being prompted for a value.
Additional optional settings are:
default
: Define a default value that is pre-selected when the user is prompted for input. The value must match the type of the argument.condition
: Tera template that is executed to determine whether this prompt is used. It is filled with all the pre-defined and user-selected variables, same as the regular templates. It must evaluate to eithertrue
orfalse
. Arguments are collected top to bottom, so only user-selected variables that were already prompted for, are available.
The name
of each argument is its key in the settings file. See the following sub-sections for
examples of how to define the arguments.
Booleans are simple binary true
/false
values, like the bool
type in Rust.
[happy]
type = "bool"
description = "Are you happy?"
default = true
Are you happy? [Yn]:
Strings are the most generic argument and allow for any valid UTF-8 content.
[name]
type = "string"
description = "What's your name?"
What's your name?:
In addition to free-form strings arguments, a validator can bespecified, that further restrains the allowed input.
Value: crate
The crate validator restricts the input to values that are considered a proper Rust crate name, according to the crates.io rules. That means precisely:
- Starts with an alphabetic character.
- All following characters are ASCII alphanumeric,
_
or-
. - The value is only up to 64 characters long.
[crate_name]
type = "string"
description = "Name a crate"
validator = "crate"
Value: ident
The ident validator ensures, the input is a Rust identifier. That means it adheres to the following rules:
- Starts with a Unicode XID Start character, and has optional following characters, that are all in the Unicode XID Continue space.
- Or starts with an underscore
_
and is followed by one or more Unicode XID Continue characters. - Is not a reserved Rust keyword.
[const_name]
type = "string"
description = "Please provide a name for the constant"
validator = "ident"
Value: semver
The semver validator verifies, that the input value is a proper semantic version. It means, it must
have the typical X.Y.Z
form and optional prerelease or build metadata values.
See the semver::Version for further details about requirements for a valid value.
[version]
type = "string"
description = "What version do you want to use?"
validator = "semver"
Value: semver_req
This validator is very similar to the semver validator, but instead allows version requirements,
rather than a singular version. It the same content that is specified in the Cargo.toml
for
dependency versions.
See the semver::VersionReq for further details.
[crate_version]
type = "string"
description = "What version do you want to use?"
validator = "semver_req"
Value: regex
The regex validator allows for any regular expression to be used as input validation. Please note that the value is defined slightly different for this validator, as it has to define the validator type and the actual regex.
Note: Usually it is recommended to use ^
and $
at the beginning and end of the regex, as it
ensures to check against the whole input. Otherwise, the input may only be partially checked and
empty values may become possible.
[name]
type = "string"
description = "Please provide a name, only in lowercase letters"
validator.regex = "^[a-z]+$"
Numbers are parsed as 64-bit integers (Rust's i64
type) and can optionally define a valid minimum
and maximum value. The boundaries are both defined as inclusive, which means up to the value
including the value itself. For example a maximum of 50
would consider the input 50
valid as
well.
min
(optional): Defines the minimum inclusive possible value.max
(optional): Defines the maximum inclusive possible value.
[age]
type = "number"
description = "How old are you?"
min = 0
max = 100
How old are you? (0..=100):
Floats work the same as numbers, but allow for 64-bit floating point values (Rust's f64
type).
Again, minimum and maximum values can optionally be set and are inclusive, meaning the defined value
is considered a valid input as well.
min
(optional): Defines the minimum inclusive possible value.max
(optional): Defines the maximum inclusive possible value.
[height]
type = "float"
description = "How tall are you (in cm)?"
min = 0.0
max = 300.0
How tall are you (in cm)? (0..=300):
Lists define a fixed set of possible input values. They're similar to Rust enums but defined as string value. The default value must be in the list of possible values or an error will be printed.
[animal]
type = "list"
description = "What's your favorite animal?"
values = [
"cat",
"dog",
"fish",
]
default = "fish"
What's your favorite animal?:
cat
dog
* fish
Multi-lists are very similar to normal lists but allow to pick multiple items at once. Again, default values must all be part of the possible values or an error will be printed. Individual elements can be (un-)selected with the spacebar or tab.
[features]
type = "multi_list"
description = "Which server features would you like to enable?"
values = [
"auth",
"compression",
"graceful-shutdown",
"logging",
]
default = [
"graceful-shutdown",
"logging",
]
Which server features would you like to enable?:
[ ] auth
[ ] compression
* [x] graceful-shutdown
[x] logging
The .hatchignore
file is identical to a .gitignore
file and supports the same patterns. It
allows to exclude certain files and directories from the pipeline. The file itself is already
excluded and doesn't need to be added to the list of ignored files.
Considering the following layout:
docs
index.md
info.md
.hatch.toml
.hatchignore
Cargo.toml
And the .hatchignore
with the following content:
/docs
The generated project would be:
Cargo.toml
.hatch.toml
and .hatchignore
are automatically excluded and the additional filter rules exclude
the docs
folder and everything withing. Therefore, only the Cargo.toml
remains.