Building a Command-Line To-Do App in Rust
In this blog post, I will create a simple command-line To-Do app in Rust. I will go through the code step-by-step, explaining how each part works. By the end, you will have a basic understanding of Rust and a functioning To-Do app.
Here is the repo for this simple To-Do app.
Prerequisites:
- Basic Rust knowledge
- Rust installed on your computer
Getting Started:
cargo new todo_app
cd todo_app
Now open src/main.rs in your favorite text editor.
Importing Necessary Modules:
We'll start by importing the necessary modules from the Rust standard library:
use std::io::{stdin, stdout, Write};
use std::collections::HashMap;
Here's what each module does:
std::io::stdin: Reads user input from the standard input stream (usually the keyboard).std::io::stdout: Accesses the standard output stream (usually the console).std::io::Write: Provides theflush()method for output streams, including stdout.std::collections::HashMap: Implements theHashMapdata structure, a key-value store.
Setting Up the Main Function and Data Structures:
main function and create the data structures for storing our tasks:fn main() {
let mut tasks: HashMap = HashMap::new();
let mut task_id_counter: u32 = 1;
HashMap to store tasks, with a unique identifier (u32) as the key and the task description (String) as the value. We also initialize a task_id_counter to assign unique IDs to tasks as they are added.The Main Loop and User Interaction:
loop {
println!("What would you like to do?");
println!("1) Add task");
println!("2) List tasks");
println!("3) Complete task");
println!("4) Quit");
let mut choice = String::new();
stdin().read_line(&mut choice).unwrap();
We use a loop to continually display the menu and read the user's choice. The stdin().read_line(&mut choice) method reads input from the user and stores it in the choice variable.
Handling User Actions:
We will use a match statement to handle the user's actions based on their input.
match choice.trim().parse::<u8>() {
We trim() the user's input to remove any leading or trailing whitespace, and then parse() it as a u8 value.
Adding a Task:
When the user selects "1", we prompt them to enter the task description.
Ok(1) => {
let mut task = String::new();
print!("Enter the task: ");
stdout().flush().unwrap();
stdin().read_line(&mut task).unwrap();
tasks.insert(task_id_counter, task.trim().to_string());
println!("Task added with ID: {}", task_id_counter);
task_id_counter += 1;
}
We read the task description, store it in the tasks HashMap, increment the task_id_counter, and display a confirmation message.
Listing Tasks:
When the user selects "2", we list the tasks. We can list the tasks in order by using a Vec.
Ok(2) => {
if tasks.is_empty() {
println!("No tasks.");
} else {
println!("Tasks:");
// Collect tasks into a Vec and sort by task ID
let mut sorted_tasks: Vec<(&u32, &String)> = tasks.iter().collect();
sorted_tasks.sort_by_key(|&(id, _)| id);
// Iterate through the sorted Vec and print the task ID and description
for (id, task) in sorted_tasks {
println!("{} - {}", id, task);
}
}
}
By using tasks.iter().collect() to create a Vec of references to the tasks, we avoid cloning the data. Then, we use sort_by_key() to sort the Vec based on the task IDs. Finally, we iterate through the sorted Vec to print the tasks in order. This approach doesn't modify the original HashMap.
Completing a Task:
When the user selects "3", we prompt them to enter the task ID to mark it as completed and remove it from the list.
Ok(3) => {
let mut task_id = String::new();
print!("Enter the task ID: ");
stdout().flush().unwrap();
stdin().read_line(&mut task_id).unwrap();
match task_id.trim().parse::<u32>() {
Ok(id) => {
if tasks.remove(&id).is_some() {
println!("Task {} completed.", id);
} else {
println!("Task not found.");
}
}
Err(_) => println!("Invalid task ID."),
}
}
We read the task ID, try to parse it as a u32, and attempt to remove the task from the tasks HashMap. If the task is successfully removed, we display a confirmation message. If the task is not found or the input is invalid, we display an error message.
Quitting the App:
Ok(4) => {
println!("Goodbye!");
break;
}
break to exit the loop, which will terminate the app. _ => {
println!("Invalid choice.");
}
}
}
}
cargo run
Comments
Post a Comment