Create Rust CLI apps with ease.

What is rust-starter?

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!

Philosopy


Table of Content


Getting Started

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.


FAQ

How does rust-starter compare to clap?
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.
What are some reasons to use rust-starter?
  • To get started quickly.
  • If you want to have CI/CD.
  • If you want to have a good integration with Github and Docker.
What are some reasons not to use rust-starter?
  • If you are not building a CLI application.
Can I contribute?
  • We welcome all contributions and ideas. Check the Github page for more details.

Features

# Workspaces

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.

  • cli : Configure and start Clap.
  • core : This is where your program code should go.
  • utils : Various utilities like logging, configuration, errors, etc...

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.

# Clap

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.

# Errors

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.

Simulating an error

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.

# Configuration

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();

# Logging

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.

# Testing

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.

# CI/CD

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

# Github Actions

Several workflows are included. Some workflows require configuration, while some others could work out of the box.

  • audit.yml: runs a security audit check and generate a report.
  • build.yml: build your project and upload the artifact as a release.
  • codecov.yml: run codecov and upload coverage report.
  • lint.yml: run clippy and generate a lint report.
  • tests.yml: run tests on github actions.
  • toc.yml: This generate a Table of Content for the Readme page, and could be removed.

# Cargo Generate

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

Contributing

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.


License

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.


Author

rust-starter is built and maintained by Abid Omar. You can reach me at .