utils.rs
Command Line Argument Parsing (clap)
In this file, we define a clap Args struct.
use std::path::PathBuf;
use clap::Parser;
use crate::utils::version;
#[derive(Parser, Debug)]#[command(author, version = version(), about)]pub struct Cli { #[arg(short, long, value_name = "FLOAT", help = "Tick rate, i.e. number of ticks per second", default_value_t = 1.0)] pub tick_rate: f64,
#[arg( short, long, value_name = "FLOAT", help = "Frame rate, i.e. number of frames per second", default_value_t = 60.0 )] pub frame_rate: f64,}This allows us to pass command line arguments to our terminal user interface if we need to.

In addition to command line arguments, we typically want the version of the command line program to
show up on request. In the clap command, we pass in an argument called version(). This
version() function (defined in src/utils.rs) uses a environment variable called
RATATUI_ASYNC_TEMPLATE_GIT_INFO to get the version number with the git commit hash.
RATATUI_ASYNC_TEMPLATE_GIT_INFO is populated in ./build.rs when building with cargo, because
of this line:
println!("cargo:rustc-env=RATATUI_ASYNC_TEMPLATE_GIT_INFO={}", git_describe);
You can configure what the version string should look like by modifying the string template code in
utils::version().
XDG Base Directory Specification
Most command line tools have configuration files or data files that they need to store somewhere. To be a good citizen, you might want to consider following the XDG Base Directory Specification.
This template uses directories-rs and ProjectDirs’s config and data local directories. You can
find more information about the exact location for your operating system here:
https://github.com/dirs-dev/directories-rs#projectdirs.
This template also prints out the location when you pass in the --version command line argument.

There are situations where you or your users may want to override where the configuration and data
files should be located. This can be accomplished by using the environment variables
RATATUI_ASYNC_TEMPLATE_CONFIG and RATATUI_ASYNC_TEMPLATE_DATA.
The functions that calculate the config and data directories are in src/utils.rs. Feel free to
modify the utils::get_config_dir() and utils::get_data_dir() functions as you see fit.
Logging
The utils::initialize_logging() function is defined in src/utils.rs. The log level is decided by
the RUST_LOG environment variable (default = log::LevelFilter::Info). In addition, the location
of the log files are decided by the RATATUI_ASYNC_TEMPLATE_DATA environment variable (default =
XDG_DATA_HOME (local)).
I tend to use .envrc and direnv for development purposes, and I have the following in my
.envrc:
export RATATUI_COUNTER_CONFIG=`pwd`/.configexport RATATUI_COUNTER_DATA=`pwd`/.dataexport RATATUI_COUNTER_LOG_LEVEL=debugThis puts the log files in the RATATUI_ASYNC_TEMPLATE_DATA folder, i.e. .data folder in the
current directory, and sets the log level to RUST_LOG, i.e. debug when I am prototyping and
developing using cargo run.

Using the RATATUI_ASYNC_TEMPLATE_CONFIG environment variable also allows me to have configuration
data that I can use for testing when development that doesn’t affect my local user configuration for
the same program.
Panic Handler
Finally, let’s discuss the initialize_panic_handler() function, which is also defined in
src/utils.rs, and is used to define a callback when the application panics. Your application may
panic for a number of reasons (e.g. when you call .unwrap() on a None). And when this happens,
you want to be a good citizen and:
- provide a useful stacktrace so that they can report errors back to you.
- not leave the users terminal state in a botched condition, resetting it back to the way it was.
In the screenshot below, I added a None.unwrap() into a function that is called on a keypress, so
that you can see what a prettier backtrace looks like:

utils::initialize_panic_handler() also calls Tui::new().exit() to reset the terminal state back
to the way it was before the user started the TUI program. We’ll learn more about the Tui in the
next section.