Create Rust CLI apps with ease.
rust-starter
is an empty Rust CLI application with libraries, and few defaults. The goal is to help you bootstrap your next CLI project as quickly as possible while ensuring you make use of the best tools and best-practices that are available today..
There is no configuration required (though we recommend you check all the possible configurations possible). An empty clone will compile, and has a few sample commands. You can start coding right away!
rust-starter
is
biased toward popular and stable depedencies. These could be changed later,
but we make these choices so that you can focus on your CLI functionalities.rust-starter
template
already compiles and runs, so you don't need to configure anything to get
your program working.rust-starter
follows the
idiomatic Rust philosophy. It attempts to follow and stick to the industry best
practices.
rust-starter
aims to keep things simple.
To get started, simply clone the repository, and run the project.
git clone https://github.com/rust-starter/rust-starter
cd rust-starter
cargo run
There are no configurations required and no initial code to write. You should get
the following output upon running cargo run
rust-starter 1.0.0
Abid Omar
CLI interface
USAGE:
rust-starter [OPTIONS] [SUBCOMMAND]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-c, --config Set a custom config file
SUBCOMMANDS:
config Show Configuration
error Simulate an error
hazard Generate a hazardous occurance
help Prints this message or the help of the given subcommand(s)
That's it. If you got this output, rust-starter
has
successfully compiled and run. You can also run the available subcommands
like cargo run config
to show the loaded configuration, cargo run hazard
which is a sample subcommand, and cargo run error
to intentionally trigger an error.
clap
and rust-starter
do not serve
the same purpose. clap
is a library to parse command-line arguments. rust-starter
is a template program that uses clap
.
rust-starter
makes use of Cargo Workspaces to manage and structure your program. It is possible to have all of the code in a single workspace and the rust-starter
template doesn't need more than one. However, starting this way will help you split code into a separate workspace when you see fit.
The main program is inside src
. Its main purpose is to start the program, the configuration, logging, and then run the arguments parser. It's not supposed to have any functionality. It is recommended that these are added through Workspaces. rust-starter
comes with 3 workspaces.
These different workspaces are libraries. It's preferrable that new workspaces can compile on their own. This makes your code modular and unit testing much simpler. You can add more workspaces if you need to.
rust-starter
uses clap to parse Command Line arguments. clap
is simple, efficient and widely used by the community.
The version used is still in beta (Version 3) but since it has non-backward compatible changes, and several useful upgrades; it might be worthwhile to use the beta until a stable version 3 is released.
clap
configuration code can be found in cli/src/lib.rs
.
rust-starter
tries to follow the best practices to handle errors. For this, it is useful that your program uses Rust's std::error::Error
trait. All your errors should implement this trait. With the 1.0 release, rust-starter
uses the failure crate; which is now deprecated. thiserror will be used instead in the future; see the note below.
To be able to propagate errors using the ?
operator, it's possible to wrap your custom error type with a Box type. This has the downside of dynamically allocating memory for errors in the heap. A better approach is to convert errors at compile time; which rust-starter
has a few examples of.
The approach taken is to define proper Result
and Error
types. This has the advantage of easy upgrades and library changes. For example, if you want to switch from the thiserror
crate, you have a central and single place to do this change, and the rest of your code is unchanged.
To test that our error reporting is functioning correctly, rust-starter
has a special subcommand to intentionally trigger an error.
cargo run error
rust-starter
uses the better-panic crate to print and prettify backtraces. This library is only enabled when debugging and disabled for release builds.
Backtracing for the std::Error
is only available in nightly. For this reason, the stable rust-starter
still uses the Failure crate. If you want to use std::Error
with Backtrace in nightly, use the newError
branch.
rust-starter
manages a configuration struct to mutate, store and retrieve a configuration state. Currently, a configuration file could be passed using the -c
flag, and its content will be merged. Environment variables starting with APP
will also be merged. It's recommended to change the APP
prefix.
Under the hood, rust-starter
uses the config-rs crate. However, it exposes an AppConfig
struct that you can use to store and retrieve configuration. The relevant code can be found in utils/src/app_config.rs
. It's recommended to use this struct instead of config-rs
directly, as this depedency might get changed in the future.
The configuration struct is initialized with a default configuration file at program start. This file can be found in src/resources/default_config.toml
. You only need to initialize the configuration once. It's stored in a static RwLock making it globally accessible and also thread-safe.
let config_contents = include_str!("resources/default_config.toml");
AppConfig::init(Some(config_contents))?;
You can merge additional settings to your configuration struct, but also retrieve or change single settings.
// Merge config
AppConfig::merge_config(cli_matches.value_of("config"))?;
// Get config value
AppConfig::get::("debug").unwrap();
// Set config value
AppConfig::set("database.url", "new url").unwrap();
The standard interface for logging in Rust is the log crate. It defines a set of macros for logging like info!
and error!
. While rust-starter
uses slog and has two drains for logs: syslog and terminal; it is fully compatible with the log facade.
Logging in rust-starter
works out of the box. In any of the sub-crates in your workspace, you can add the log crate as a depedency and use its macros. These logs will be forwarded to slog drains. If these drains are not available or fail to initialize, the logs will be discarded.
To see logging in action, you can run the error
command. It should log an error message to the terminal and syslog. Before the program fails, you should see this line. (This should also be logged to syslog).
Nov 10 01:19:42.014 INFO We are simulating an error, who: rust-starter
While this is good enough, you might want to customize your logging drains. Any logs that you write, are passed to all of these drains. slog supports several drain options. The file utils/src/logger.rs
contains these implementations.
rust-starter
comes with a few tests for the commandline program and the config struct. You can run all the tests by running.
cargo test --all
It is recommended to put integration tests for the command line in the root of your program, and write tests for each sub-crate in your workspace individually.
rust-starter
also has integrations to run tests in Github actions, and also a code coverage integration with codecov.
rust-starter
is not optimized for any particular use-case; and should, preferrably, run in multiple operating systems under different conditions. Unfortunately, it's currently compiled only against Linux. macOS and Windows are high in the list of the OSes to be supported in the next releases.
Currently, rust-starter
compiles under rust-musl-builder. rust-musl-builder
is a docker image to compile your rust application into a static Rust binary. It is then tested under Alpine Linux to make sure your program is working inside the lightest of containers.
While compiling locally is faster for development, this doesn't give many garantuees about your program running under different conditions. This is less important if you are deploying your program to a predictable environment (ie: a server). You can, then, change the Docker file to match the environment for your deployment.
On the other hand, if you are distributing the program under different conditions as a binary, it might be a good idea to have it compile as a static standalone binary. To do that, all you have to run is the compile command for that. You'll need to have Docker and just installed in your system.
just build-image
A docker image is created under the name of name:version
. If you didn't change the default values, it should be rust-starter:1.0.0
. To run this image, you can execute.
docker run rust-starter:1.0.0
If you have a Docker hub account, you can push your image to the docker hub cloud. First, you should edit the dockerhub_org
variable in the justfile
and then execute the push command.
just push-image
Several workflows are included. Some workflows require configuration, while some others could work out of the box.
cargo-generate is a developer tool to help you get up and running quickly with a new Rust project by leveraging a pre-existing git repository as a template. cargo-generate doesn't have any templates by default. A template for rust-starter is available at rust-starter-generate.
cargo generate --git https://github.com/rust-starter/rust-starter-generate
We welcome any kind of contributions. Whether you are reporting a bug, coding a feature or correcting a typo. Every effort counts; and all contributions are greatly appreaciated.
Details on how to contribute can be found in CONTRIBUTING.md.
rust-starter is licensed under the MIT License. Dependent libraries and crates have their own license. An analyze of licensing for these libraries is still under-work.
The logo is sourced from Flaticon.
rust-starter
is built and maintained by Abid Omar.
You can reach me at .