adding documentation
This commit is contained in:
@@ -2,6 +2,12 @@
|
||||
name = "manager_employees_rust"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
authors = ["Carlos Gutierrez <cgutierrez44833@ucumberlands.edu>"]
|
||||
description = "Assignment 4: Implementing Control Structures — Rust backend (Axum/Postgres) for employee scheduling (7 days x 3 shifts, preference-aware, 2-employees-per-shift enforcement)"
|
||||
license = "MIT"
|
||||
repository = "https://git.cargdev.io/CarGDev/manager_employees_rust"
|
||||
keywords = ["rust", "axum", "postgres", "scheduling", "control-structures"]
|
||||
categories = ["web-programming::http-server"]
|
||||
|
||||
[dependencies]
|
||||
axum = "0.8.4"
|
||||
|
||||
@@ -1,2 +1,287 @@
|
||||
# manager_employees_rust
|
||||
# manager_employees_rust — Backend (Rust)
|
||||
|
||||
> **Assignment 4: Implementing Control Structures — Rust Backend API**
|
||||
> University of the Cumberlands — Master’s Program, Semester 4 / Week 4
|
||||
|
||||
[](https://www.rust-lang.org/)
|
||||
[](https://github.com/tokio-rs/axum)
|
||||
[](https://github.com/launchbadge/sqlx)
|
||||
[](./LICENSE)
|
||||
|
||||
**Author:** Carlos Gutierrez — [cgutierrez44833@ucumberlands.edu](mailto:cgutierrez44833@ucumberlands.edu)
|
||||
|
||||
**Repository:** [`https://git.cargdev.io/CarGDev/manager_employees_rust`](https://git.cargdev.io/CarGDev/manager_employees_rust) — **this repo is the Rust backend**
|
||||
|
||||
**Related Repositories (Assignment 4 — 3 implementations):**
|
||||
| Implementation | Language / Role | Repository |
|
||||
|---|---|---|
|
||||
| **This repo** | **Rust — Backend API (Axum/Postgres)** | [`manager_employees_rust`](https://git.cargdev.io/CarGDev/manager_employees_rust) |
|
||||
| Companion | Python — Backend/CLI | [`manager_employees_python`](https://git.cargdev.io/CarGDev/manager_employees_python) |
|
||||
| Companion | GUI — Graphical Interface | [`manager_employees_gui`](https://git.cargdev.io/CarGDev/manager_employees_gui) |
|
||||
|
||||
---
|
||||
|
||||
## 📌 This Repository — Rust Backend Only
|
||||
|
||||
**This repo contains ONLY the Rust backend** for Assignment 4. The assignment requires implementing the same scheduling problem in **two contrasting languages** (e.g., Rust + Python) to demonstrate control flow across paradigms — with an optional GUI.
|
||||
|
||||
* **Scope of this repo:** REST API built with `Axum` (`src/router.rs:159`), `Tokio` async runtime, `SQLx` + Postgres persistence (`src/database/connection.rs:5`), and a preference-aware weekly scheduling engine (`src/router.rs:561`, `src/manager/`).
|
||||
* **Companion repos** are **not** tracked here — see table above: Python implementation and GUI implementation live at `git.cargdev.io/CarGDev`.
|
||||
* API docs are served via `utoipa` + Swagger UI at `/swagger-ui` (`src/router.rs:187`).
|
||||
|
||||
---
|
||||
|
||||
## 📖 Story: Assignment 4 — Implementing Control Structures
|
||||
|
||||
### 1. Overview
|
||||
|
||||
> *You will implement, without logical errors, various control structures such as conditionals, loops, and branching, in multiple programming languages. This assignment will demonstrate your mastery of syntax and control flow in different paradigms.*
|
||||
> — Assignment 4 Prompt
|
||||
|
||||
For me, Carlos Gutierrez, this assignment was about proving that the same business rules can be expressed cleanly in two very different paradigms. I chose **Rust** for this backend because its `match`, `if let`, ownership model, and `Result` error handling force explicit, safe control flow — a sharp contrast to a dynamic language like Python or JavaScript. Every scheduling rule below is therefore not just business logic, but a deliberate exercise in conditionals, loops, and branching done idiomatically in Rust.
|
||||
|
||||
### 2. Application Prompt — Employee Schedule Manager
|
||||
|
||||
The company operates **7 days/week** with **3 shifts/day** (`morning`, `afternoon`, `evening` — `src/database/types.rs:18`). The app must:
|
||||
|
||||
1. **Input and Storage:** Collect employee names and their preferred shift for each day, storing in appropriate structures. Here: `users` + `employee_weekly_preferences` + `company_users` tables (`src/database/schema.rs:36`, `src/database/entities/`), with requests like `CreateEmployeeRequest` and `WeeklyPreferenceRequest` (`src/router.rs:32`, `src/router.rs:46`).
|
||||
|
||||
2. **Scheduling Logic:** Auto-assign shifts ensuring:
|
||||
* No employee works more than **one shift per day** (`src/manager/validator.rs:5`, `src/router.rs:585`)
|
||||
* Max **5 days per week** per employee (`src/manager/validator.rs:11`, `src/router.rs:586`)
|
||||
* At least **2 employees per shift per day**; if fewer are available, randomly/round-robin assign additional valid employees (`src/main.rs:37`, `src/manager/scheduler.rs:25`, `src/router.rs:597`)
|
||||
|
||||
3. **Shift Conflicts:** Detect when a preferred shift is full and resolve by assigning to another available shift on the **same or next day** (`src/manager/conflict.rs:4`, `src/manager/conflict.rs:10`, `src/manager/conflict.rs:16`, `src/router.rs:240`).
|
||||
|
||||
4. **Output:** Final weekly schedule in readable format indicating employee → day → shift. Here: `GET /schedules/weekly` and `print_final_schedule()` grouping by day/shift (`src/manager/scheduler.rs:44`, `src/router.rs:542`).
|
||||
|
||||
5. **Bonus — Priority Ranking:** Employees rank shifts `first > second > third` (`src/database/types.rs:18`, `src/database/schema.rs:70`). The engine ranks candidates with `preference_rank()` (`src/router.rs:240`) and accommodates preferences while still meeting coverage.
|
||||
|
||||
### 3. Programming Language Choice
|
||||
|
||||
> Suggested: Python, Java, C++, JavaScript, Go, Rust — *must be contrasting paradigms*.
|
||||
|
||||
This repo = **Rust** (systems, compiled, ownership + exhaustive `match`) at [`manager_employees_rust`](https://git.cargdev.io/CarGDev/manager_employees_rust).
|
||||
My contrasting implementations are:
|
||||
* **Python:** [`manager_employees_python`](https://git.cargdev.io/CarGDev/manager_employees_python) — dynamic, interpreted, idiomatic loops/comprehensions
|
||||
* **GUI:** [`manager_employees_gui`](https://git.cargdev.io/CarGDev/manager_employees_gui) — graphical input/output for employee names, preferences, and final schedule (bonus requirement)
|
||||
|
||||
The Rust side emphasizes `match` over `if-else` chains, iterator loops (`for day in all_days()` `src/router.rs:581`), and `Result`-based branching for DB errors (`src/app.rs:29`) — a direct contrast to Python’s approach in the companion repo.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
|
||||
* 7-day × 3-shift auto-generation via `POST /schedules/weekly/generate` (`src/router.rs:561`)
|
||||
* Preference-aware ranking (`first=0, second=1, third=2, fallback=3`) (`src/router.rs:240`)
|
||||
* Validation: one-shift/day, ≤5 days/week, ≥2/shift (`src/manager/validator.rs`)
|
||||
* Conflict resolution: same-day fallback → next-day (`src/manager/conflict.rs`)
|
||||
* Clock-in/out (`POST /employees/{id}/clock-in` `src/router.rs:627`, `PUT /employees/{id}/clock-out` `src/router.rs:652`)
|
||||
* Company & employee CRUD, weekly preferences (`PUT /employees/{id}/weekly-preferences` `src/router.rs:465`)
|
||||
* Health check `GET /health` (`src/router.rs:262`), CORS permissive (`src/router.rs:188`), OpenAPI docs
|
||||
|
||||
---
|
||||
|
||||
## 🛠 Tech Stack
|
||||
|
||||
| Layer | Choice | Version / File |
|
||||
|-------|--------|----------------|
|
||||
| Language | Rust | `edition = "2024"` (`Cargo.toml:4`) |
|
||||
| Web | Axum | `0.8.4` (`Cargo.toml:7`) |
|
||||
| Async | Tokio | `1.53.1` full (`Cargo.toml:13`) |
|
||||
| DB | Postgres + SQLx | `0.9.0` chrono/postgres/runtime-tokio (`Cargo.toml:12`) |
|
||||
| Validation/Types | Chrono, Serde | `chrono 0.4.42`, `serde 1.0.228` (`Cargo.toml:8`, `Cargo.toml:10`) |
|
||||
| Docs | utoipa + utoipa-swagger-ui | `5` / `9` (`Cargo.toml:15`, `Cargo.toml:16`) |
|
||||
| Middleware | tower-http CORS | `0.6.6` (`Cargo.toml:14`) |
|
||||
| Env | dotenvy | `0.15.7` (`Cargo.toml:9`) |
|
||||
|
||||
---
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
main.rs # entry, env loading, pool init, serve (src/main.rs:10)
|
||||
app.rs # AppState { pool }, ApiError → 404/500 (src/app.rs:10)
|
||||
router.rs # 16 routes, OpenAPI, scheduling core (src/router.rs:159)
|
||||
database/
|
||||
connection.rs # connect() + init_database() (src/database/connection.rs:5)
|
||||
schema.rs # 9 DDL queries: enums + 5 tables + alters (src/database/schema.rs:94)
|
||||
types.rs # WorkTime, DayOfWeek enums (src/database/types.rs:18)
|
||||
entities/ # Company, User, CompanyUser, Schedule, WeeklyPreference
|
||||
queries/ # company, employee, preference, schedule, user
|
||||
manager/ # business-rule stubs extracted from main.rs assignment_rules()
|
||||
scheduler.rs # generate_weekly_schedule, fill_missing, clock-in/out
|
||||
validator.rs # same-day, 5-day, 2-employee validations
|
||||
conflict.rs # detect + resolve same/next day
|
||||
models/
|
||||
company/ # CreateCompanyRequest + service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗄 Database Schema
|
||||
|
||||
Enums: `work_time('morning','afternoon','evening')` (`src/database/schema.rs:1`), `day_of_week('sunday'..'saturday')` (`src/database/schema.rs:10`)
|
||||
|
||||
| Table | Key Columns |
|
||||
|-------|-------------|
|
||||
| `companies` | `id SERIAL PK, name TEXT UNIQUE` (`src/database/schema.rs:27`) |
|
||||
| `users` | `id, name, last_name, preference work_time` (`src/database/schema.rs:36`) |
|
||||
| `company_users` | `user_id FK→users, company_id FK→companies, UNIQUE(user_id,company_id)` (`src/database/schema.rs:47`) |
|
||||
| `schedules` | `user_id FK, enter_time, day_of_week, out_time, shift work_time` (`src/database/schema.rs:58`) |
|
||||
| `employee_weekly_preferences` | `user_id, day_of_week, first/second/third work_time, UNIQUE(user_id,day_of_week)` (`src/database/schema.rs:70`) |
|
||||
|
||||
Init via `CREATE_SCHEMA_QUERIES` loop (`src/database/connection.rs:13`).
|
||||
|
||||
---
|
||||
|
||||
## 📐 Business Rules in Code
|
||||
|
||||
* `assignment_rules()` summary (`src/main.rs:29-39`)
|
||||
* `all_days()` / `all_shifts()` helpers (`src/router.rs:224`, `src/router.rs:236`)
|
||||
* Candidate filtering + `sort_by_key(preference_rank)` (`src/router.rs:589-595`)
|
||||
* `assigned_days: HashMap<(user_id, DayOfWeek), bool>` + `day_count_by_user: HashMap<user_id, i32>` (`src/router.rs:576`)
|
||||
|
||||
---
|
||||
|
||||
## 🔌 API Reference
|
||||
|
||||
Base: `http://127.0.0.1:5005` (configurable via `APP_ADDRESS`)
|
||||
|
||||
| Method | Path | Tag | Description |
|
||||
|--------|------|-----|-------------|
|
||||
| `GET` | `/health` | health | `{"status":"ok"}` (`src/router.rs:254`) |
|
||||
| `POST` | `/companies` | companies | create (`src/router.rs:276`) |
|
||||
| `GET` | `/companies` | companies | list (`src/router.rs:284`) |
|
||||
| `GET` | `/companies/{id}` | companies | get (`src/router.rs:310`) |
|
||||
| `DELETE` | `/companies/{id}` | companies | delete (`src/router.rs:319`) |
|
||||
| `POST` | `/employees` | employees | create + assign to company (`src/router.rs:350`) |
|
||||
| `GET` | `/employees` | employees | list (`src/router.rs:375`) |
|
||||
| `POST` | `/companies/{company_id}/employees/{employee_id}` | employees | assign (`src/router.rs:394`) |
|
||||
| `DELETE` | `/employees/{id}` | employees | delete (`src/router.rs:418`) |
|
||||
| `PUT` | `/employees/{id}/preference` | employees | single pref (`src/router.rs:441`) |
|
||||
| `PUT` | `/employees/{id}/weekly-preferences` | employees | 7-day prefs (`src/router.rs:465`) |
|
||||
| `GET` | `/companies/{company_name}/employees/names` | employees | names by company (`src/router.rs:499`) |
|
||||
| `POST` | `/schedules` | schedules | create (`src/router.rs:518`) |
|
||||
| `GET` | `/schedules/weekly` | schedules | list all (`src/router.rs:542`) |
|
||||
| `POST` | `/schedules/weekly/generate` | schedules | auto-generate (`src/router.rs:561`) |
|
||||
| `POST` | `/employees/{id}/clock-in` | schedules | clock-in now (`src/router.rs:627`) |
|
||||
| `PUT` | `/employees/{id}/clock-out` | schedules | clock-out (`src/router.rs:652`) |
|
||||
|
||||
Swagger: `GET /swagger-ui` + `GET /api-docs/openapi.json` (`src/router.rs:187`).
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* Rust `stable` (with `cargo`)
|
||||
* Postgres ≥14 running locally
|
||||
* `sqlx-cli` optional (schema is auto-created on boot)
|
||||
|
||||
### 1. Clone & Configure
|
||||
|
||||
```bash
|
||||
git clone https://git.cargdev.io/CarGDev/manager_employees_rust
|
||||
cd manager_employees_rust
|
||||
cp .env.example .env # or create .env
|
||||
```
|
||||
|
||||
> See also companions: [`manager_employees_python`](https://git.cargdev.io/CarGDev/manager_employees_python) and [`manager_employees_gui`](https://git.cargdev.io/CarGDev/manager_employees_gui).
|
||||
|
||||
`.env` (`src/main.rs:11` loads via `dotenvy`):
|
||||
|
||||
```env
|
||||
DATABASE_URL=postgres://carlos:password@localhost/rust_manager_employees
|
||||
APP_ADDRESS=127.0.0.1:5005
|
||||
```
|
||||
|
||||
> `.env` is gitignored (` .gitignore:7`). Example URL from `.env:1`.
|
||||
|
||||
### 2. Create Database
|
||||
|
||||
```bash
|
||||
createdb rust_manager_employees
|
||||
# or: psql -c "CREATE DATABASE rust_manager_employees;"
|
||||
```
|
||||
|
||||
Tables/enums are created automatically on first run via `init_database()` (`src/database/connection.rs:12`).
|
||||
|
||||
### 3. Run
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
# → listening on 127.0.0.1:5005
|
||||
curl http://127.0.0.1:5005/health
|
||||
# {"status":"ok"}
|
||||
```
|
||||
|
||||
### 4. Try the Flow
|
||||
|
||||
```bash
|
||||
# 1. Create company
|
||||
curl -X POST http://127.0.0.1:5005/companies \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"Acme"}'
|
||||
|
||||
# 2. Create employees (company_id from above)
|
||||
curl -X POST http://127.0.0.1:5005/employees \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"company_id":1,"name":"Ana","last_name":"Lopez","preference":"morning"}'
|
||||
|
||||
# 3. Set weekly preferences
|
||||
curl -X PUT http://127.0.0.1:5005/employees/1/weekly-preferences \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"preferences":[{"day_of_week":"monday","first":"morning","second":"afternoon","third":"evening"}]}'
|
||||
|
||||
# 4. Generate weekly schedule
|
||||
curl -X POST http://127.0.0.1:5005/schedules/weekly/generate \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"company_id":1}'
|
||||
|
||||
# 5. View schedule
|
||||
curl http://127.0.0.1:5005/schedules/weekly
|
||||
# Swagger UI: http://127.0.0.1:5005/swagger-ui
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
| Var | Required | Default | Description |
|
||||
|-----|----------|---------|-------------|
|
||||
| `DATABASE_URL` | yes | — | Postgres URL (`src/main.rs:13`) |
|
||||
| `APP_ADDRESS` | no | `127.0.0.1:5005` | Bind addr (`src/main.rs:19`) |
|
||||
|
||||
---
|
||||
|
||||
## 👨💻 Development
|
||||
|
||||
```bash
|
||||
cargo fmt
|
||||
cargo clippy
|
||||
cargo check
|
||||
cargo test
|
||||
```
|
||||
|
||||
Error handling: `ApiError(sqlx::Error)` → `RowNotFound→404`, else `500` with JSON `{"error": "..."}` (`src/app.rs:29`).
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT — Copyright (c) 2026 Carlos Gutierrez. See [LICENSE](./LICENSE).
|
||||
|
||||
---
|
||||
|
||||
## 📬 Contact
|
||||
|
||||
**Carlos Gutierrez** — University of the Cumberlands
|
||||
Email: [cgutierrez44833@ucumberlands.edu](mailto:cgutierrez44833@ucumberlands.edu)
|
||||
Project: `manager_employees_rust` — Rust backend for Assignment 4: Implementing Control Structures
|
||||
Repositories:
|
||||
* Rust Backend (this repo): <https://git.cargdev.io/CarGDev/manager_employees_rust>
|
||||
* Python Backend: <https://git.cargdev.io/CarGDev/manager_employees_python>
|
||||
* GUI: <https://git.cargdev.io/CarGDev/manager_employees_gui>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "manager_employees_rust",
|
||||
"version": "0.1.0",
|
||||
"description": "Assignment 4: Implementing Control Structures — Rust backend (Axum/Postgres) for employee scheduling",
|
||||
"author": "Carlos Gutierrez <cgutierrez44833@ucumberlands.edu>",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.cargdev.io/CarGDev/manager_employees_rust"
|
||||
},
|
||||
"relatedRepositories": {
|
||||
"rust_backend": "https://git.cargdev.io/CarGDev/manager_employees_rust",
|
||||
"python_backend": "https://git.cargdev.io/CarGDev/manager_employees_python",
|
||||
"gui": "https://git.cargdev.io/CarGDev/manager_employees_gui"
|
||||
},
|
||||
"keywords": [
|
||||
"rust",
|
||||
"axum",
|
||||
"tokio",
|
||||
"postgres",
|
||||
"sqlx",
|
||||
"scheduling",
|
||||
"employee-scheduling",
|
||||
"employee-management",
|
||||
"control-structures",
|
||||
"conditionals",
|
||||
"loops",
|
||||
"branching",
|
||||
"backend",
|
||||
"api",
|
||||
"assignment-4",
|
||||
"university-of-the-cumberlands"
|
||||
],
|
||||
"engines": {
|
||||
"rust": ">=1.85"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user