This milestone project will provide an excellent opportunity to consolidate your learning and showcase your mastery of Rust programming. It combines concepts from each guide into a real-world application, allowing you to see how they interact and complement each other in a practical setting.
Happy coding!
Step 1: Initialize the project
Run:
cargo new task_manager
When this is created, open the project in your code editor.
Step 2: Create a module named task
Inside of the src/
directory, create a directory named task, then create a mod.rs
file and a task.rs
file:
.
├── Cargo.toml
└── src/
├── main.rs
└── task/
├── mod.rs
└── task.rs
Step 3: Add the chrono
crate for working with time
Run:
cargo add chrono
When it is done installing the crate, check that chrono
and its version are recorded in the [dependencies]
section of the Cargo.toml
file.
Step 4: Create the Task struct
Create the Task
struct inside of task.rs
to handle task descriptions.
pub struct Task {
pub title: String,
pub description: String,
pub due_date: chrono::NaiveDate,
pub completed: bool,
}
impl Task {
pub fn new(title: String, description: String, due_date: chrono::NaiveDate) -> Self {
Task {
title,
description,
due_date,
completed: false,
}
}
}
Note: You can one of the ways an imported crate is used. But we will also have to call it properly in the main.rs
file, as you will soon see. Also, we are yet to explore advanced method implementations, but this is what it looks like. Remember to export the task.rs
file inside mod.rs
:
pub mod task;
Step 5: Create the main program
Use the task module inside the main program:
pub mod task;
pub use task::task::Task;
use std::io;
use chrono::prelude::Local;
fn main() {
println!("Welcome to Rusty Task Manager!\\n");
let tasks: Vec<Task> = Vec::new();
loop {
println!("Commands:");
println!("- add <title> <description> <due_date>");
println!("- view");
println!("- complete <task_index>");
println!("- filter <completed | upcoming>\\n");
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read input");
let parts: Vec<&str> = input.trim().split_whitespace().collect();
match parts[0] {
// ... rest of the code remains the same
"filter" => {
if parts.len() == 2 {
match parts[1] {
"completed" => {
for (index, task) in tasks.iter().enumerate().filter(|t| t.1.completed) {
println!("{}. {} [Complete]", index + 1, task.title);
}
}
"upcoming" => {
let today = Local::today().naive_local();
for (index, task) in tasks.iter().enumerate().filter(|t| t.1.due_date >= today) {
let status = if task.completed { "[Complete]" } else { "[Incomplete]" };
println!("{}. {} {} (Due: {})", index + 1, task.title, status, task.due_date);
}
}
_ => println!("Invalid filter option."),
}
} else {
println!("Invalid input. Use: filter <completed | upcoming>");
}
}
_ => println!("Invalid command."),
}
}
}
Step 6: Run the project
Run:
cargo run
The project should run as expected. There you go! You have built your first real-world application in Rust.
Note: You can run cargo build
instead to make Cargo build a binary file for the project with the same name as the project name. Then you can change the directory into target/debug/
, where there is a task_manager
binary executable that you can run like this:
./task_manager
You can tweak this project to find ways to improve it. Otherwise, see you in the Intermediate Rust series!