-
Notifications
You must be signed in to change notification settings - Fork 0
Rafaeljohn9/cargo tracker #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f4af88f
46e6de1
b7a37cf
7545d84
3714969
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,218 @@ | ||||||||||||||||||||||||||||||
| mod shipment; | ||||||||||||||||||||||||||||||
| mod shipment_manager; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| use crate::shipment::{Package, ShipmentStatus}; | ||||||||||||||||||||||||||||||
| use crate::shipment_manager::ShipmentManager; | ||||||||||||||||||||||||||||||
| use std::io::{self, Write}; | ||||||||||||||||||||||||||||||
| use std::process::exit; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| fn print_help() { | ||||||||||||||||||||||||||||||
| println!("Available commands:"); | ||||||||||||||||||||||||||||||
| println!(" add-shipment - Add a new shipment with tracking ID and destination"); | ||||||||||||||||||||||||||||||
| println!(" add-package - Add a package to an existing shipment"); | ||||||||||||||||||||||||||||||
| println!(" update-status - Update the status of a shipment"); | ||||||||||||||||||||||||||||||
| println!(" view-shipment - View details of a specific shipment"); | ||||||||||||||||||||||||||||||
| println!(" list-shipments - List all shipments (optionally filter by status)"); | ||||||||||||||||||||||||||||||
| println!(" generate-report - Generate shipment/package reports"); | ||||||||||||||||||||||||||||||
| println!(" clear - Clear the screen"); | ||||||||||||||||||||||||||||||
| println!(" help - Show available commands"); | ||||||||||||||||||||||||||||||
| println!(" exit - Exit the Cargo Tracker CLI"); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| fn read_input(prompt: &str) -> String { | ||||||||||||||||||||||||||||||
| print!("{}", prompt); | ||||||||||||||||||||||||||||||
| io::stdout().flush().unwrap(); | ||||||||||||||||||||||||||||||
| let mut input = String::new(); | ||||||||||||||||||||||||||||||
| io::stdin().read_line(&mut input).unwrap(); | ||||||||||||||||||||||||||||||
| input.trim().to_string() | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| fn main() { | ||||||||||||||||||||||||||||||
| println!("Hello, world!"); | ||||||||||||||||||||||||||||||
| println!("Welcome to Cargo Tracker 1.0!"); | ||||||||||||||||||||||||||||||
| println!("Type 'help' to see a list of available commands."); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| let mut manager = ShipmentManager::new(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| loop { | ||||||||||||||||||||||||||||||
| let command = read_input("> ").to_lowercase(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| match command.as_str() { | ||||||||||||||||||||||||||||||
| "help" => print_help(), | ||||||||||||||||||||||||||||||
| "add-shipment" => { | ||||||||||||||||||||||||||||||
| // Create a new shipment | ||||||||||||||||||||||||||||||
| let tracking_id = read_input("Please enter the Tracking ID: "); | ||||||||||||||||||||||||||||||
| if manager.get_shipment(&tracking_id).is_some() { | ||||||||||||||||||||||||||||||
| println!( | ||||||||||||||||||||||||||||||
| "Error: Shipment with tracking ID '{}' already exists.", | ||||||||||||||||||||||||||||||
| tracking_id | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| let destination = read_input("Please enter the destination: "); | ||||||||||||||||||||||||||||||
| let status = ShipmentStatus::Pending; | ||||||||||||||||||||||||||||||
| let time_of_departure = Some(chrono::Utc::now()); | ||||||||||||||||||||||||||||||
| let shipment_id = if tracking_id.is_empty() { | ||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||
| Some(tracking_id.clone()) | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
| let shipment = | ||||||||||||||||||||||||||||||
| manager.create_shipment(status, destination, time_of_departure, shipment_id); | ||||||||||||||||||||||||||||||
| println!("Shipment Created\n\n"); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Let's add packages to the shipment | ||||||||||||||||||||||||||||||
| println!("Let's add packages. Type 'q' to quit"); | ||||||||||||||||||||||||||||||
| let mut count = 0; | ||||||||||||||||||||||||||||||
| let mut package_num = 1; | ||||||||||||||||||||||||||||||
| loop { | ||||||||||||||||||||||||||||||
| let prompt = format!("Enter package #{} description: ", package_num); | ||||||||||||||||||||||||||||||
| let description = read_input(&prompt); | ||||||||||||||||||||||||||||||
| if description.trim().eq_ignore_ascii_case("q") { | ||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| let package = Package::new(description); | ||||||||||||||||||||||||||||||
| shipment.add_package(package); | ||||||||||||||||||||||||||||||
| count += 1; | ||||||||||||||||||||||||||||||
| package_num += 1; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| let tracking = &shipment.tracking_id; | ||||||||||||||||||||||||||||||
| println!( | ||||||||||||||||||||||||||||||
| "{} package{} added to shipment '{}'.", | ||||||||||||||||||||||||||||||
| count, | ||||||||||||||||||||||||||||||
| if count == 1 { "" } else { "s" }, | ||||||||||||||||||||||||||||||
| tracking | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| "add-package" => { | ||||||||||||||||||||||||||||||
| // Add a package to an existing shipment | ||||||||||||||||||||||||||||||
| let tracking_id = read_input("Enter the Tracking ID of the shipment: "); | ||||||||||||||||||||||||||||||
| if tracking_id.is_empty() { | ||||||||||||||||||||||||||||||
| println!("Tracking ID cannot be empty."); | ||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| match manager.get_shipment(&tracking_id) { | ||||||||||||||||||||||||||||||
| Some(shipment) => { | ||||||||||||||||||||||||||||||
| let description = read_input("Enter package description: "); | ||||||||||||||||||||||||||||||
| if description.trim().is_empty() { | ||||||||||||||||||||||||||||||
| println!("Package description cannot be empty."); | ||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| let package = Package::new(description); | ||||||||||||||||||||||||||||||
| shipment.add_package(package); | ||||||||||||||||||||||||||||||
| println!("Package added to shipment '{}'.", shipment.tracking_id); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| None => { | ||||||||||||||||||||||||||||||
| println!("Shipment with Tracking ID '{}' not found.", tracking_id); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| "update-status" => { | ||||||||||||||||||||||||||||||
| let tracking_id = read_input("Please enter the Tracking ID: "); | ||||||||||||||||||||||||||||||
| match manager.get_shipment(&tracking_id) { | ||||||||||||||||||||||||||||||
| Some(shipment) => { | ||||||||||||||||||||||||||||||
| let status_input = | ||||||||||||||||||||||||||||||
| read_input("Enter new status (Pending, InTransit, Delivered, Lost): "); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| let new_status = match status_input.trim().to_lowercase().as_str() { | ||||||||||||||||||||||||||||||
| "pending" => ShipmentStatus::Pending, | ||||||||||||||||||||||||||||||
| "intransit" => ShipmentStatus::InTransit, | ||||||||||||||||||||||||||||||
| "delivered" => { | ||||||||||||||||||||||||||||||
| shipment.time_of_arrival = Some(chrono::Utc::now()); | ||||||||||||||||||||||||||||||
| ShipmentStatus::Delivered | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| "lost" => ShipmentStatus::Lost, | ||||||||||||||||||||||||||||||
| _ => { | ||||||||||||||||||||||||||||||
| println!( | ||||||||||||||||||||||||||||||
| "Error: Invalid status. Valid options are: Pending, InTransit, Delivered, Lost." | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
|
Comment on lines
+124
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not exit the CLI on invalid status; continue the loop.
- _ => {
- println!(
- "Error: Invalid status. Valid options are: Pending, InTransit, Delivered, Lost."
- );
- return;
- }
+ _ => {
+ println!(
+ "Error: Invalid status. Valid options are: Pending, InTransit, Delivered, Lost."
+ );
+ continue;
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| shipment.status = new_status.clone(); | ||||||||||||||||||||||||||||||
| println!( | ||||||||||||||||||||||||||||||
| "Shipment '{}' status updated to {:?}.", | ||||||||||||||||||||||||||||||
| shipment.tracking_id, new_status | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| None => { | ||||||||||||||||||||||||||||||
| println!("Shipment with Tracking ID '{}' not found.", tracking_id); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| "view-shipment" => { | ||||||||||||||||||||||||||||||
| let tracking_id = read_input("Please enter the Tracking ID: "); | ||||||||||||||||||||||||||||||
| match manager.get_shipment(&tracking_id) { | ||||||||||||||||||||||||||||||
| Some(shipment) => { | ||||||||||||||||||||||||||||||
| println!("Tracking ID: {}", shipment.tracking_id); | ||||||||||||||||||||||||||||||
| println!("Destination: {}", shipment.destination); | ||||||||||||||||||||||||||||||
| println!("Status: {:?}", shipment.status); | ||||||||||||||||||||||||||||||
| println!("Packages:"); | ||||||||||||||||||||||||||||||
| for package in &shipment.packages { | ||||||||||||||||||||||||||||||
| println!("({}) - {}", package.id, package.description); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| println!(); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| None => { | ||||||||||||||||||||||||||||||
| println!("Shipment with Tracking ID '{}' not found.", tracking_id); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| "list-shipments" => { | ||||||||||||||||||||||||||||||
| // List all shipments, optionally filter by status | ||||||||||||||||||||||||||||||
| let filter = read_input( | ||||||||||||||||||||||||||||||
| "Filter by status; Pending, InTransit, Delivered, Lost: (leave empty for all): ", | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| let filter_trimmed = filter.trim().to_lowercase(); | ||||||||||||||||||||||||||||||
| let status_filter = match filter_trimmed.as_str() { | ||||||||||||||||||||||||||||||
| "" => None, | ||||||||||||||||||||||||||||||
| "pending" => Some(ShipmentStatus::Pending), | ||||||||||||||||||||||||||||||
| "intransit" => Some(ShipmentStatus::InTransit), | ||||||||||||||||||||||||||||||
| "delivered" => Some(ShipmentStatus::Delivered), | ||||||||||||||||||||||||||||||
| "lost" => Some(ShipmentStatus::Lost), | ||||||||||||||||||||||||||||||
| _ => { | ||||||||||||||||||||||||||||||
| println!("No shipments found for status '{}'.", filter); | ||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
| let filtered_shipments = manager.list_shipments(status_filter); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if filtered_shipments.is_empty() { | ||||||||||||||||||||||||||||||
| println!("No shipments found."); | ||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||
| for shipment in filtered_shipments { | ||||||||||||||||||||||||||||||
| println!( | ||||||||||||||||||||||||||||||
| "Tracking ID: {} | Destination: {} | Status: {:?} | Packages: {} | Departure: {} | Arrival: {}", | ||||||||||||||||||||||||||||||
| shipment.tracking_id, | ||||||||||||||||||||||||||||||
| shipment.destination, | ||||||||||||||||||||||||||||||
| shipment.status, | ||||||||||||||||||||||||||||||
| shipment.packages.len(), | ||||||||||||||||||||||||||||||
| shipment | ||||||||||||||||||||||||||||||
| .time_of_departure | ||||||||||||||||||||||||||||||
| .map(|t| t.to_rfc3339()) | ||||||||||||||||||||||||||||||
| .unwrap_or_else(|| "N/A".to_string()), | ||||||||||||||||||||||||||||||
| shipment | ||||||||||||||||||||||||||||||
| .time_of_arrival | ||||||||||||||||||||||||||||||
| .map(|t| t.to_rfc3339()) | ||||||||||||||||||||||||||||||
| .unwrap_or_else(|| "N/A".to_string()), | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| "generate-report" => { | ||||||||||||||||||||||||||||||
| // TODO: Implement generate-report logic | ||||||||||||||||||||||||||||||
| println!("(generate-report not yet implemented)"); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| "clear" => { | ||||||||||||||||||||||||||||||
| // Clear screen (works on most Unix terminals) | ||||||||||||||||||||||||||||||
| print!("\x1B[2J\x1B[1;1H"); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| "exit" => { | ||||||||||||||||||||||||||||||
| println!("Goodbye!"); | ||||||||||||||||||||||||||||||
| exit(0); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| "" => continue, | ||||||||||||||||||||||||||||||
| _ => println!("'{}' is not a valid command.", command), | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| use chrono::{DateTime, Utc}; | ||
| use serde_json::json; | ||
| use uuid::Uuid; | ||
|
|
||
| #[derive(Debug)] | ||
| pub struct Shipment { | ||
| pub tracking_id: String, | ||
| pub destination: String, | ||
| pub packages: Vec<Package>, | ||
| pub status: ShipmentStatus, | ||
| pub time_of_departure: Option<DateTime<Utc>>, | ||
| pub time_of_arrival: Option<DateTime<Utc>>, | ||
| } | ||
|
|
||
| impl Shipment { | ||
| pub fn new( | ||
| status: ShipmentStatus, | ||
| destination: String, | ||
| time_of_departure: Option<DateTime<Utc>>, | ||
| tracking_id: String, | ||
| ) -> Self { | ||
| Self { | ||
| packages: Vec::new(), | ||
| tracking_id, | ||
| destination, | ||
| status, | ||
| time_of_departure, | ||
| time_of_arrival: None, | ||
| } | ||
| } | ||
|
|
||
| pub fn add_package(&mut self, package: Package) { | ||
| self.packages.push(package); | ||
| } | ||
|
|
||
| pub fn to_json_str(&self) -> String { | ||
| let packages_json = self | ||
| .packages | ||
| .iter() | ||
| .map(|p| { | ||
| json!({ | ||
| "id": p.id.to_string(), | ||
| "description": p.description, | ||
| }) | ||
| }) | ||
| .collect::<Vec<_>>(); | ||
|
|
||
| let json_obj = json!({ | ||
| "tracking_id": self.tracking_id, | ||
| "destination": self.destination, | ||
| "status": format!("{:?}", self.status), | ||
| "time_of_departure": self.time_of_departure.map(|t| t.to_rfc3339()), | ||
| "time_of_arrival": self.time_of_arrival.map(|t| t.to_rfc3339()), | ||
| "packages": packages_json, | ||
| }); | ||
|
|
||
| json_obj.to_string() | ||
| } | ||
|
Comment on lines
+36
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Refactor to use serde's Serialize derive instead of manual JSON construction. Manually building JSON is error-prone and harder to maintain. Since Add serde to Cargo.toml if not already present with the derive feature: serde = { version = "1", features = ["derive"] }Then refactor the structs: +use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
-use serde_json::json;
use uuid::Uuid;
-#[derive(Debug)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Shipment {
pub tracking_id: String,
pub destination: String,
pub packages: Vec<Package>,
pub status: ShipmentStatus,
pub time_of_departure: Option<DateTime<Utc>>,
pub time_of_arrival: Option<DateTime<Utc>>,
}-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ShipmentStatus {
Pending,
InTransit,
Delivered,
Lost,
}-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Package {
pub id: Uuid,
pub description: String,
}Replace the entire pub fn to_json_str(&self) -> String {
- let packages_json = self
- .packages
- .iter()
- .map(|p| {
- json!({
- "id": p.id.to_string(),
- "description": p.description,
- })
- })
- .collect::<Vec<_>>();
-
- let json_obj = json!({
- "tracking_id": self.tracking_id,
- "destination": self.destination,
- "status": format!("{:?}", self.status),
- "time_of_departure": self.time_of_departure.map(|t| t.to_rfc3339()),
- "time_of_arrival": self.time_of_arrival.map(|t| t.to_rfc3339()),
- "packages": packages_json,
- });
-
- json_obj.to_string()
+ serde_json::to_string(self).unwrap_or_else(|_| String::from("{}"))
} |
||
| } | ||
|
|
||
| #[derive(Debug, Clone, PartialEq)] | ||
| pub enum ShipmentStatus { | ||
| Pending, | ||
| InTransit, | ||
| Delivered, | ||
| Lost, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone)] | ||
| pub struct Package { | ||
| pub id: Uuid, | ||
| pub description: String, | ||
| } | ||
|
|
||
| impl Package { | ||
| pub fn new(description: String) -> Self { | ||
| Self { | ||
| id: Uuid::new_v4(), | ||
| description, | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| use crate::shipment::{Shipment, ShipmentStatus}; | ||
| use std::collections::HashMap; | ||
|
|
||
| pub struct ShipmentManager { | ||
| shipments: HashMap<String, Shipment>, | ||
| } | ||
|
|
||
| impl ShipmentManager { | ||
| pub fn new() -> Self { | ||
| ShipmentManager { | ||
| shipments: HashMap::new(), | ||
| } | ||
| } | ||
|
|
||
| pub fn create_shipment( | ||
| &mut self, | ||
| status: ShipmentStatus, | ||
| destination: String, | ||
| time_of_departure: Option<chrono::DateTime<chrono::Utc>>, | ||
| tracking_id: Option<String>, | ||
| ) -> &mut Shipment { | ||
| let id = tracking_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); | ||
| let shipment = Shipment::new(status, destination, time_of_departure, id.clone()); | ||
| self.shipments.insert(id.clone(), shipment); | ||
| self.shipments.get_mut(&id).unwrap() | ||
| } | ||
|
|
||
| pub fn get_shipment(&mut self, tracking_id: &str) -> Option<&mut Shipment> { | ||
| self.shipments.get_mut(tracking_id) | ||
| } | ||
|
|
||
| /// List all shipments, with optional status filter. | ||
| pub fn list_shipments(&self, status_filter: Option<ShipmentStatus>) -> Vec<&Shipment> { | ||
| self.shipments | ||
| .values() | ||
| .filter(|s| { | ||
| if let Some(ref status) = status_filter { | ||
| &s.status == status | ||
| } else { | ||
| true | ||
| } | ||
| }) | ||
| .collect() | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.