This file contains information of use to developers wanting to use a Terracotta project template in their own projects. The rules and information contained here should serve as a good baseline for any project.
The main sections in this document are:
- Getting started
- Setup
- Codebase structure
- API endpoint structure
- API endpoint commit checklist
- Coding standards
- Coverage
The Terracotta project template repositories are set up as a template repositories on GitHub, so that you can easily click the "Use this template" button to create a new repository based on it. You can then clone your new repository and start working on it. This will give you a starting point where you have all the project files, but none of the commit history. This is the currently-recommended way to start a new project using Terracotta (there are plans to also have a command-line tool at a later date).
You may instead decide that you want to fork the repository, or clone it and then push it to a new repository. This is also fine, but you should be aware of the following points:
- You will have the full commit history, which may be useful, but it is specifically relevant to Terracotta, and so mention of e.g. release versions will be misleading as your project will most likely maintain its own, independent versioning scheme.
- You will also have the various release version tags created on the relevant Terracotta project template repository, which will be misleading for the same reason, and likely conflict with your own tags.
- There is no particular advantage to maintaining a Git tree association with the Terracotta project template as an upstream repository, and as the development you do on your application will lead to conflicts, you are best not be pulling in updates for it. You should instead apply any updates manually. This is the same as when using other web application scaffolds.
- Forks on GitHub are treated as subsidiaries of the original repository, and not first-class repositories in their own right. For this reason, commits made to forks don't count as contributions in user profiles, which is not a desirable situation if you are starting a new project.
For these reasons, forking in the GitHub-recognised sense is not recommended, and cloning and pushing to a new repository is only recommended if you are comfortable with the caveats mentioned above.
To see an example of a project that has been created based on Terracotta, you can look at the Rustmark application. This shows how Terracotta can be used as a starting point, and then extended to create a more complex application. Note that in the case of Rustmark, the decision was made to actually fork Terracotta at a stage before its initial release, as the commit history was considered useful, and there were no release commits or tags to cause the conflict issues mentioned above. However, after that point of inception, all Terracotta updates have been applied manually, and it is not a "true" fork in GitHub terms.
The steps to set up a Terracotta project are simple and standard. You need a reasonably-recent Rust environment, on a Linux or Mac machine. There are currently no special requirements beyond what is needed to build a standard Rust project.
Note that these instructions are for building the application yourself, which will usually be in context of having used Terracotta as a template for a new project. In this case these steps will apply for your project too.
There are some key points to note about the environment you choose:
- Debian and Ubuntu are the Linux distros of choice, although other distros should also work just fine, as there are no special requirements.
- Running natively on MacOS works fine.
- Running natively on Windows is not targeted or tested, and there are no plans to support it, so although it may work, it also may not. Running on WSL does work fine, and is the recommended way to run on Windows.
Typically, you will set up Rust using rustup
, which is the
recommended way to install Rust. The stable
toolchain is targeted, as the
focus is on stability and correctness, rather than bleeding-edge features.
Once you have Rust installed, you can build the project using cargo build
.
This will download and compile all dependencies, and build the project. You can
then run the project using cargo run
.
Because configuration applies to the examples as well as to a new project, the various options are covered in the Configuration section of the examples documentation.
A Terracotta-based project can typically be run using the cargo run
command,
or by running the compiled binary directly. The server will listen on port 8000
by default, and will serve content from the static
directory, plus any request
handlers that you define. The static
directory contains the static files to be
served.
You can run the test suite using cargo test
. This will run all unit and
integration tests.
Note that, at present, there are very few tests written specifically for this project, as it is mostly a combination of other crates from the Rust ecosystem. Those tests that do exist are intended to be examples of approach, and are not exhaustive. Additional tests might be added when the project is more mature, and sensible things to test have been clearly identified.
You can build the developer documentation using cargo doc
. This will generate
HTML files and place them into target/doc
. You can then open the documentation
in your browser by opening target/doc/terracotta/index.html
.
Building the documentation for local development use will also provide you with links to the source code.
You can find information on how to deploy a Terracotta-based project in the documentation for each project template. The README files have details about Docker, for example.
The code in the examples and template repositories follows a simple and straightforward layout, which is intended to be easy to understand and extend. You should absolutely modify the file structure to suit the shape of your own web application.
The basic folder structure is:
docs
: This contains documentation.html
: This is where all the HTML templates reside, to be processed by Tera.src
: This is where all the Rust code lives.static
: This is where any static files should go, which are public and do not require authentication.
The layout of each folder should be fairly self-explanatory, but it is worth
mentioning that the src
folder represents the simplest sensible minimum. In a
proper application it is likely that the handlers should be split out into more
files, and there would also be various other supporting files too. Rather than
dictate a layout, it is left as an exercise for the reader to implement their
preferred approach.
Any images and other files that need to be protected by authentication should be
placed in the content
directory. Public images should be placed in
static/img
, and will be served from the /img
URL path, similar to the CSS,
JS, and WebFont files.
All of the content and static material is included in the compiled binary, making it very straightforward to deploy.
Machine-consumable endpoints should be placed under a path of /api
. Those that
have application functionality should be versioned, and placed under a path of
/api/vX
, where X
is the version number. For example, the first version of
the API is placed under /api/v1
.
This section contains a checklist of things that are mandatory to do when adding a new API endpoint.
For each endpoint added, the following need to also be added:
- Rustdocs
- Unit tests
- OpenAPI documentation
- Written documentation
For each commit made, the following need to pass without errors or warnings:
cargo build
cargo clippy
cargo doc
cargo test
Before a new endpoint can be declared complete:
- A coverage report needs to be run and checked. See the Coverage section for more details.
The code in this repository follows some specific and opinionated coding standards. These mostly follow typical community conventions, but notable differences are the use of tabs for indentation, the alignment of various terms to aid readability, the use of comment headers to separate sections of code, and the usage of Nerd Font symbols in those headers to belay semantic meaning in order to apply highlighting.
You may well dislike aspects of the coding style, which is fine — feel free to change things, and make the code your own! Individuality is important.
Since Rust 1.60.0, coverage is supported natively. This means that there is no need to use external tools such as Tarpaulin or kcov to generate coverage reports, which is a huge improvement.
On a Debian or Ubuntu system, you will need to install the grcov
package. You
will also need to install the llvm-tools-preview
component for Rust, and
create a directory to store the coverage reports in.
sudo apt install grcov
rustup component add llvm-tools-preview
mkdir coverage
The following commands will run the tests and generate coverage reports. The profile files are then deleted, as they are not needed. The commands will generate reports in HTML and LCOV formats, the latter of which can be loaded into various tools.
Note that the --binary-path
is important, and needs to point to your build
directory. By default this will be under ./target
, but if you have changed
this, e.g. to store builds in a central location, then you will need to adjust
the path accordingly.
CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='cargo-test-%p-%m.profraw' cargo test
grcov . --binary-path ./target/debug/deps/ -s . -t html --branch --ignore-not-existing --ignore '../*' --ignore "/*" -o coverage/html
grcov . --binary-path ./target/debug/deps/ -s . -t lcov --branch --ignore-not-existing --ignore '../*' --ignore "/*" -o coverage/tests.lcov
find . -name "*.profraw" -delete
The HTML report can be viewed by opening coverage/html/index.html
in your
browser.