Anatomy of a Rust Program, Part III: /src/lib.rs

This blog post is the third in a series that describes the structure of the wink command line program that I have written in rust and that I use to invoke other Windows and Linux programs from bash and Windows shells under Windows Subsystem for Linux. This post describes the components defined in the /src/lib.rs file. This file contains implementations relevant to the contents of the /src/main.rs file, specifically its main() function.

This file contains far to much code to list here. The following are important statements:

pub mod wsl;

This effectively parses the contents of /src/wsl.rs here, bringing its components into scope. To avoid a potential namespace conflict, it is important that this statement not also appear in another crate in the package, such as /src/main.rs.

The following statements define rust modules at this path (the crate root) from the corresponding files, where modules contain the crate::helperror::HelpError and crate::winkconfig::WinkConfig structs and the crate::wsl library. Again, keeping the HelpError and WinkConfig type definitions in their own files seems to be against convention, although there are easy techniques to make it easier to shorten the namespace references.

pub mod helperror; // /src/helperror.rs defines helperror::HelpError
pub mod winkconfig; // /src/winkconfig.rs defines winkconfig::WinkConfig
pub mod wsl; // /wsl.rs defines the contents of wsl::

The following statements allow use of tokens such as InvocableCategory without the long path prefixes.

use crate::wsl::inv::invocablecategory::InvocableCategory; // /src/wsl/inv/invocablecategory.rs
use crate::wsl::inv::invocablecategorylist::InvocableCategoryList; // /src/wsl/inv/invocablecategorylist.rs
use crate::wsl::inv::invoker::Invoker; // /src/wsl/inv/invoker.rs

The run() function in /src/lib.rs contains the body of the program invoked by the main() function in /src/main.rs. It accepts a WinkConfig (/src/winkconfig.rs) named config and an InvocableCategoryList (/src/wsl/inv/invocablecategorylist.rs) named category_list and returns an integer to use as an operating system exit code for the program.

pub fn run(config: crate::winkconfig::WinkConfig, category_list: InvocableCategoryList) -> i32 {

The run function passes the command code entered on the command line to the get_invocable method of the InvocableCategoryList named category_list.

if let Some(invocable) = category_list.get_invocable(&config.command_code) {

The get_invocable() function iterates through the Invocables in its InvocableCategories to find one that matches the command code specified on the command line. The get_invocable() function returns an Option, which is a rust enumeration that contains of either Some or None. Some contains a value; None does not. If the get_invocable() function returns Some, then it contains the Invocable to invoke (or, depending on command line parameters, export as JSON, or for which to output the command line it would run to stdout), and the inner block runs.

        if config.export {
            if config.pretty_print {
                println!("{}", serde_json::to_string_pretty(&invocable).unwrap());
            } else {
                println!("{}", serde_json::to_string(&invocable).unwrap());
            }
        }

        let invoker = Invoker {};
        invoker.invoke(invocable, config.dry_run, config.verbose, config.cmd_args);
        return 0;
    } else if config.export && config.command_code.is_empty() {
        if config.pretty_print {
            println!("{}", serde_json::to_string_pretty(&category_list).unwrap());
        } else {
            println!("{}", serde_json::to_string(&category_list).unwrap());
        }

        return 0;
    } else if (config.command_code.is_empty() || !config.export) && config.dry_run {
        return 0;
    }

    help(
        &format!("Command not recognized: {0}", config.command_code),
        config,
        category_list.categories,
    )
}

If a matching Invocable exists, and if command line arguments specify, the run() function serializes the Invocable as JSON to stdout.

Next, run() creates an Invoker (/src/wsl/inv/invoker.rs) and passes the Invocable retrieved from match and configuration data retrieved earlier to the invoke() function of that Invoker, runs calls the operating system to run the command. The run() function then returns 0 as the value to return to the operating system to indicate successful program execution. If no Invoker matches the command code specified on the command line, then the run() function calls the help() function also defined in /src/lib.wsl to display usage information.

The help() function does not warrant much description. It mainly uses the print! and println!() macros to write to stdout and the color() function defined in /src/lib.wsl to vary some parts of its output. It iterates the categories of Invocable and displays their contents.

2 thoughts on “Anatomy of a Rust Program, Part III: /src/lib.rs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: