Building Command Line Interface (CLI) tools is a popular use of the Rust programming language, thanks to its performance and safety guarantees. Rust supports a variety of programming paradigms, including object-oriented programming (OOP) via traits and structs. This article will guide you through creating interactive CLI tools in Rust by leveraging OOP-like patterns.
Setting Up Your Rust Project
Before we begin coding, make sure you have Rust installed on your machine. You can do this by following the instructions at rustup.rs. Once Rust is installed, you can create a new project:
cargo new cli_toolThis will set up a new Rust project named cli_tool.
Defining the CLI Tool Structure
In OOP, we typically start by designing classes. In Rust, we use structs and traits to achieve similar encapsulation and abstraction. Below is a simple definition of a Command struct that we'll use to represent our CLI commands.
// src/main.rs
struct Command {
name: String,
description: String,
execute: fn(),
}
impl Command {
fn new(name: &str, description: &str, execute: fn()) -> Command {
Command {
name: name.to_string(),
description: description.to_string(),
execute,
}
}
}
Here we defined a Command struct with fields for the command name, description, and a function pointer to handle execution.
Implementing a Trait for Execution
Let's enforce an interface for command execution using traits. This provides similar functionality to interfaces in OOP languages.
trait Executable {
fn execute(&self);
}
impl Executable for Command {
fn execute(&self) {
(self.execute)();
}
}
Here, the Executable trait defines a method, execute(), which we must implement for any struct that implements this trait.
Designing Interactive CLI Input
Now let’s give some interactivity to our CLI tool by incorporating user input. We'll update our main function to interactively handle user commands.
use std::io::{self, Write};
fn main() {
let hello_command = Command::new(
"hello",
"Prints 'Hello, world!'",
|| println!("Hello, world!"),
);
let commands: Vec> = vec![Box::new(hello_command)];
loop {
print!("Enter a command: ");
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let input = input.trim();
for command in &commands {
if command.name == input {
command.execute();
break;
}
}
if input == "exit" {
break;
}
}
}
In this example, we've created an interactive loop that accepts user input and tries to match it to one of our commands. We also handle an "exit" command to break the loop.
Expanding Your Toolset
To expand your CLI tool, you can define more commands by following the Command struct pattern and adding them to the commands list. Additionally, consider adding error handling and user feedback for better interaction and debugging.
This article has demonstrated how to use Rust's struct and trait system to create an interactive CLI tool with OOP-like flexibility and reusability. By continuing to apply these patterns, you can build robust command-line applications suited for a variety of tasks, from quick automation scripts to comprehensive command suites.