Conversation
There was a problem hiding this comment.
Pull request overview
This pull request contains multiple week submissions (Week 2, 3, 4, and 5) for a Rust learning curriculum. The primary focus is Week 5 Day 1, which introduces a Rust Scaffold CLI tool that generates opinionated project structures for Axum and Actix-web frameworks.
Changes:
- Week 5 Day 1: New Rust Scaffold CLI project with template-based project generation for web frameworks
- Week 4 Day 1: Blog API project using Axum with CRUD operations for blog posts
- Week 3 Day 3: CLI grep-like tool for searching text in files
- Week 2 submissions: Expense tracker and user input handling exercises
Reviewed changes
Copilot reviewed 36 out of 41 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| submissions/week-5/day-1/stephenngozi-rust-scaffold-cli/Cargo.toml | Project manifest with invalid Rust edition "2024" |
| submissions/week-5/day-1/stephenngozi-rust-scaffold-cli/src/main.rs | CLI entry point with command routing |
| submissions/week-5/day-1/stephenngozi-rust-scaffold-cli/src/cli.rs | CLI argument parsing using clap |
| submissions/week-5/day-1/stephenngozi-rust-scaffold-cli/src/framework.rs | Framework abstraction with significant code duplication |
| submissions/week-5/day-1/stephenngozi-rust-scaffold-cli/src/handlers.rs | User interaction and prompt handling |
| submissions/week-5/day-1/stephenngozi-rust-scaffold-cli/src/services.rs | File system operations for scaffolding |
| submissions/week-5/day-1/stephenngozi-rust-scaffold-cli/src/templates/mod.rs | Shared template constants |
| submissions/week-5/day-1/stephenngozi-rust-scaffold-cli/src/templates/axum.rs | Axum framework templates |
| submissions/week-5/day-1/stephenngozi-rust-scaffold-cli/src/templates/actix.rs | Actix-web framework templates |
| submissions/week-4/day-1/stephen-ngozi-blog-project/Cargo.toml | Blog project manifest with invalid edition |
| submissions/week-4/day-1/stephen-ngozi-blog-project/src/models.rs | Data models with missing AppState definition |
| submissions/week-4/day-1/stephen-ngozi-blog-project/src/app_state.rs | Disconnected AppState module |
| submissions/week-4/day-1/stephen-ngozi-blog-project/src/router.rs | Route definitions with incorrect path syntax |
| submissions/week-4/day-1/stephen-ngozi-blog-project/src/config.rs | Configuration with incorrect error variant usage |
| submissions/week-4/day-1/stephen-ngozi-blog-project/src/error.rs | Error types with spelling errors |
| submissions/week-3/day-3/stephenngozi-build-cli-project/Cargo.toml | CLI grep project with invalid edition |
| submissions/week-3/day-3/stephenngozi-build-cli-project/src/main.rs | Main entry with spelling error |
| submissions/week-2/day-5/Stephen-Ngozi-Merkle-Tree/Cargo.toml | Expense tracker with invalid edition |
| submissions/week-2/day-5/Stephen-Ngozi-Merkle-Tree/src/main.rs | Main file with multiple spelling errors |
| submissions/week-2/day-3/Stephen-Ngozi-User-Input/Cargo.toml | User input project with invalid edition |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let id: u8 = id_input.trim().parse().expect("Please enter a valid ID"); | ||
|
|
||
| if tracker.delete(id){ | ||
| println!(" Your Expence is Deleted "); |
There was a problem hiding this comment.
Spelling error: "Expence" should be "Expense".
| println!(" Your Expence is Deleted "); | |
| println!(" Your Expense is Deleted "); |
| tracker.save_to_file("stephen_expences.txt"); | ||
| println!("Data saved to stephen_expences.txt"); |
There was a problem hiding this comment.
Spelling error: "expences" should be "expenses".
| tracker.save_to_file("stephen_expences.txt"); | |
| println!("Data saved to stephen_expences.txt"); | |
| tracker.save_to_file("stephen_expenses.txt"); | |
| println!("Data saved to stephen_expenses.txt"); |
| content: payload.content, | ||
| author_id: payload.author_id, | ||
| created_at: now, | ||
| updated_at:now |
There was a problem hiding this comment.
Missing space after colon in struct field. The formatting should be "updated_at: now" for consistency with Rust style guidelines.
| updated_at:now | |
| updated_at: now |
| let args: Vec<String> = env::args().collect(); | ||
|
|
||
| let config = Config::new(&args).unwrap_or_else(|err |{ | ||
| eprintln!("Problem parsing argumemts: {}", err); |
There was a problem hiding this comment.
Spelling error: "argumemts" should be "arguments".
| eprintln!("Problem parsing argumemts: {}", err); | |
| eprintln!("Problem parsing arguments: {}", err); |
| } | ||
| } | ||
| } else if input == "3" { | ||
| println!("Enter the ID of the Expence"); |
There was a problem hiding this comment.
Spelling error: "Expence" should be "Expense".
| pub fn dependencies(&self) -> String { | ||
| match self { | ||
| Framework::Axum => { | ||
| r#"[dependencies] | ||
| # Core web framework built on Tokio — fast and ergonomic | ||
| axum = "0.7" | ||
|
|
||
| # Async runtime — required for all async Rust code | ||
| tokio = { version = "1", features = ["full"] } | ||
|
|
||
| # Tower middleware ecosystem (axum is built on top of tower) | ||
| tower = "0.4" | ||
| tower-http = { version = "0.5", features = ["cors", "trace"] } | ||
|
|
||
| # Serialization / Deserialization — JSON support | ||
| serde = { version = "1", features = ["derive"] } | ||
| serde_json = "1" | ||
|
|
||
| # Environment variable management | ||
| dotenv = "0.15" | ||
|
|
||
| # Logging | ||
| tracing = "0.1" | ||
| tracing-subscriber = { version = "0.3", features = ["env-filter"] } | ||
| "# | ||
| .to_string() | ||
| } | ||
|
|
||
| Framework::Actix => { | ||
| r#"[dependencies] | ||
| # Core web framework — high-performance, battle-tested | ||
| actix-web = "4" | ||
|
|
||
| # Tokio async runtime (actix-web uses it under the hood) | ||
| tokio = { version = "1", features = ["full"] } | ||
|
|
||
| # Middleware utilities for actix-web | ||
| actix-cors = "0.7" | ||
|
|
||
| # Serialization / Deserialization — JSON support | ||
| serde = { version = "1", features = ["derive"] } | ||
| serde_json = "1" | ||
|
|
||
| # Environment variable management | ||
| dotenv = "0.15" | ||
|
|
||
| # Logging | ||
| env_logger = "0.11" | ||
| log = "0.4" | ||
| "# | ||
| .to_string() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Returns the content of main.rs for each framework. | ||
| pub fn main_rs_content(&self, project_name: &str) -> String { | ||
| match self { | ||
| Framework::Axum => format!( | ||
| r#"mod handlers; | ||
| mod models; | ||
| mod router; | ||
| mod services; | ||
|
|
||
| use tracing_subscriber::{{EnvFilter, fmt}}; | ||
|
|
||
| // CONCEPT: #[tokio::main] is a macro that sets up the Tokio async runtime. | ||
| // Without it, you can't use `async fn main()`. | ||
| #[tokio::main] | ||
| async fn main() {{ | ||
| // Initialize tracing/logging | ||
| fmt() | ||
| .with_env_filter(EnvFilter::from_default_env()) | ||
| .init(); | ||
|
|
||
| // Load environment variables from .env file | ||
| dotenv::dotenv().ok(); | ||
|
|
||
| tracing::info!("Starting {} server...", "{project_name}"); | ||
|
|
||
| // Build the router — all routes are registered in router/mod.rs | ||
| let app = router::create_router(); | ||
|
|
||
| // Bind to address and start serving | ||
| let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") | ||
| .await | ||
| .expect("Failed to bind to port 3000"); | ||
|
|
||
| tracing::info!("🚀 Server running at http://0.0.0.0:3000"); | ||
|
|
||
| axum::serve(listener, app) | ||
| .await | ||
| .expect("Server error"); | ||
| }} | ||
| "#, | ||
| project_name = project_name | ||
| ), | ||
|
|
||
| Framework::Actix => format!( | ||
| r#"mod handlers; | ||
| mod models; | ||
| mod router; | ||
| mod services; | ||
|
|
||
| use actix_web::{{App, HttpServer}}; | ||
|
|
||
| // CONCEPT: #[actix_web::main] sets up the Actix async runtime. | ||
| // Actix has its own runtime built on top of Tokio. | ||
| #[actix_web::main] | ||
| async fn main() -> std::io::Result<()> {{ | ||
| // Initialize logger | ||
| env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); | ||
|
|
||
| // Load environment variables from .env file | ||
| dotenv::dotenv().ok(); | ||
|
|
||
| log::info!("Starting {} server...", "{project_name}"); | ||
| log::info!("🚀 Server running at http://0.0.0.0:8080"); | ||
|
|
||
| // HttpServer::new takes a closure that returns an App | ||
| // The move || closure captures environment for thread safety | ||
| HttpServer::new(move || {{ | ||
| App::new() | ||
| .configure(router::configure_routes) | ||
| }}) | ||
| .bind("0.0.0.0:8080")? | ||
| .run() | ||
| .await | ||
| }} | ||
| "#, | ||
| project_name = project_name | ||
| ), | ||
| } | ||
| } | ||
|
|
||
| // Returns the router/mod.rs content | ||
| pub fn router_mod_content(&self) -> String { | ||
| match self { | ||
| Framework::Axum => { | ||
| r#"use axum::Router; | ||
| // Import your handlers here as you build them out. | ||
| // Example: use crate::handlers::health::health_check; | ||
|
|
||
| /// Creates and returns the main application Router. | ||
| /// Add all your routes here using `.route(path, method_handler)`. | ||
| pub fn create_router() -> Router { | ||
| Router::new() | ||
| // Example routes — uncomment and expand as needed: | ||
| // .route("/health", axum::routing::get(handlers::health_check)) | ||
| // .route("/api/v1/users", axum::routing::get(handlers::users::list_users)) | ||
| } | ||
| "# | ||
| .to_string() | ||
| } | ||
|
|
||
| Framework::Actix => { | ||
| r#"use actix_web::web; | ||
| // Import your handlers here as you build them out. | ||
| // Example: use crate::handlers::health::health_check; | ||
|
|
||
| /// Configures all routes for the Actix-web application. | ||
| /// Called in main.rs inside App::new().configure(...) | ||
| pub fn configure_routes(cfg: &mut web::ServiceConfig) { | ||
| cfg | ||
| // Example routes — uncomment and expand as needed: | ||
| // .service(web::resource("/health").route(web::get().to(handlers::health_check))) | ||
| // .service( | ||
| // web::scope("/api/v1") | ||
| // .service(web::resource("/users").route(web::get().to(handlers::users::list_users))) | ||
| // ) | ||
| ; | ||
| } | ||
| "# | ||
| .to_string() | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| // Returns the handlers/mod.rs content | ||
| pub fn handlers_mod_content(&self) -> &'static str { | ||
| match self { | ||
| Framework::Axum => axum::HANDLERS_MOD, | ||
| Framework::Actix => actix::HANDLERS_MOD, | ||
| } | ||
| } | ||
|
|
||
| // Returns the .env content | ||
| pub fn env_content(&self) -> &'static str { | ||
| match self { | ||
| Framework::Axum => "RUST_LOG=info\nPORT=3000\n", | ||
| Framework::Actix => "RUST_LOG=info\nPORT=8080\n", | ||
| } | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
There is significant code duplication between the framework.rs file and the template files. The dependencies method in framework.rs (lines 28-81) contains the exact same content as the DEPENDENCIES constants in templates/axum.rs and templates/actix.rs. Similarly, main_rs_content (lines 84-161), router_mod_content (lines 164-204) duplicate content from the template modules. This violates the DRY (Don't Repeat Yourself) principle and makes maintenance harder. Consider removing the duplicated code from framework.rs and instead using the constants from the templates module directly.
| use sqlx::PgPool; | ||
| use std::sync::Arc; | ||
|
|
||
|
|
||
|
|
||
| #[derive(Debug,Clone)] | ||
|
|
||
| // pub struct AppState { | ||
| // pub db_pool: PgPool, | ||
| // } | ||
| pub struct AppState { | ||
| pub authors: Arc<RwLock<HashMap<uuid::Uuid,Author>>>, | ||
| pub posts: Arc<RwLock<HashMap<uuid::Uuid, BlogPost>>>, | ||
| // next_id: Arc<RwLock<u64>>, | ||
| } | ||
|
|
||
| impl AppState { | ||
| pub fn new() -> Self { | ||
| Self { | ||
| authors: Arc::new(RwLock::new(seed_authors())), | ||
| posts: Arc::new(RwLock::new(HashMap::new())), | ||
| // next_id: Arc::new(RwLock::new(0u64)), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Default for AppState { | ||
| fn default() -> Self { | ||
| Self::new() | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
The app_state module is not declared in main.rs, so this file is not part of the compilation unit. The code in this file references types (RwLock, HashMap, Author, BlogPost) and a function (seed_authors) that are either not imported or not accessible. This will prevent the project from compiling. To fix this, either integrate this code into models.rs or add "mod app_state;" to main.rs and ensure all necessary imports are present.
| pub async fn insert_post ( | ||
| state: &AppState, | ||
| payload: CreatePostRequest,) | ||
| -> Result<BlogPost, AppError> | ||
|
|
||
| { |
There was a problem hiding this comment.
Missing space after comma in function parameters. The formatting should be "state: &AppState, payload: CreatePostRequest" for better readability and consistency with Rust style guidelines.
| pub async fn insert_post ( | |
| state: &AppState, | |
| payload: CreatePostRequest,) | |
| -> Result<BlogPost, AppError> | |
| { | |
| pub async fn insert_post( | |
| state: &AppState, payload: CreatePostRequest, | |
| ) -> Result<BlogPost, AppError> | |
| { |
| fn seed_authors() -> HashMap<Uuid, Author> { | ||
| let authors = vec![ | ||
| Author::new( | ||
| Uuid::new_v4(), | ||
| "Emeka Allison".to_string(), | ||
| "emekaallison4@gmail.com".to_string(), | ||
| ), | ||
| Author::new( | ||
| Uuid::new_v4(), | ||
| "John Doe".to_string(), | ||
| "johndoe@mail.com".to_string(), | ||
| ), | ||
| ]; | ||
|
|
||
| authors.into_iter().map(|a| (a.id, a)).collect() | ||
| } |
There was a problem hiding this comment.
The AppState struct is not defined in this file, but it is imported from models in other files (e.g., handlers.rs, services.rs). The actual AppState definition exists in app_state.rs but that module is not connected to the main.rs module tree. Additionally, the seed_authors function defined on line 77 is used by AppState::new() in app_state.rs but is not accessible because it's in a different module. This will cause compilation errors. Consider either: 1) Moving AppState and seed_authors to this file and making seed_authors public, or 2) Adding app_state as a module in main.rs and adjusting imports accordingly.
| InternalServerError(String), | ||
| #[error("Invalid Input, cannot be processed: {field} - {message}")] | ||
| UnProcessableEntity { field: String, message: String }, | ||
| #[error("Environement Variable is missing: {0}")] |
There was a problem hiding this comment.
Spelling error: "Environement" should be "Environment".
| #[error("Environement Variable is missing: {0}")] | |
| #[error("Environment Variable is missing: {0}")] |
Task Submission PR
Student Info:
Task Details:
Checklist Before Submitting:
Additional Comments:
If any, mention here.