use axum::{
extract::{Path, State},
http::StatusCode,
response::Json,
routing::{get, post, delete},
Router,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use tokio::net::TcpListener;
// ============================================
// Models
// ============================================
#[derive(Debug, Clone, Serialize)]
pub struct User {
pub id: u32,
pub email: String,
pub name: String,
pub created_at: String,
}
#[derive(Debug, Deserialize)]
pub struct CreateUserRequest {
pub email: String,
pub name: String,
}
#[derive(Debug, Serialize)]
pub struct ApiResponse<T> {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
impl<T> ApiResponse<T> {
fn success(data: T) -> Self {
Self { success: true, data: Some(data), error: None }
}
fn error(msg: &str) -> Self {
Self { success: false, data: None, error: Some(msg.to_string()) }
}
}
// ============================================
// Database (Thread-Safe)
// ============================================
#[derive(Default)]
pub struct Database {
users: RwLock<HashMap<u32, User>>,
next_id: RwLock<u32>,
}
impl Database {
pub fn new() -> Self {
Self {
users: RwLock::new(HashMap::new()),
next_id: RwLock::new(1),
}
}
pub fn create_user(&self, req: CreateUserRequest) -> User {
let mut next_id = self.next_id.write().unwrap();
let id = *next_id;
*next_id += 1;
let user = User {
id,
email: req.email,
name: req.name,
created_at: chrono::Utc::now().to_rfc3339(),
};
self.users.write().unwrap().insert(id, user.clone());
user
}
pub fn get_user(&self, id: u32) -> Option<User> {
self.users.read().unwrap().get(&id).cloned()
}
pub fn get_all_users(&self) -> Vec<User> {
self.users.read().unwrap().values().cloned().collect()
}
pub fn delete_user(&self, id: u32) -> bool {
self.users.write().unwrap().remove(&id).is_some()
}
}
type AppState = Arc<Database>;
// ============================================
// Handlers
// ============================================
async fn health() -> Json<serde_json::Value> {
Json(serde_json::json!({
"status": "healthy",
"timestamp": chrono::Utc::now().to_rfc3339()
}))
}
async fn get_users(State(db): State<AppState>) -> Json<ApiResponse<Vec<User>>> {
let users = db.get_all_users();
Json(ApiResponse::success(users))
}
async fn create_user(
State(db): State<AppState>,
Json(req): Json<CreateUserRequest>,
) -> (StatusCode, Json<ApiResponse<User>>) {
if req.email.is_empty() || req.name.is_empty() {
return (StatusCode::BAD_REQUEST, Json(ApiResponse::error("Email and name required")));
}
let user = db.create_user(req);
(StatusCode::CREATED, Json(ApiResponse::success(user)))
}
async fn get_user(
State(db): State<AppState>,
Path(id): Path<u32>,
) -> Result<Json<ApiResponse<User>>, (StatusCode, Json<ApiResponse<User>>)> {
match db.get_user(id) {
Some(user) => Ok(Json(ApiResponse::success(user))),
None => Err((StatusCode::NOT_FOUND, Json(ApiResponse::error("User not found")))),
}
}
async fn delete_user_handler(
State(db): State<AppState>,
Path(id): Path<u32>,
) -> StatusCode {
if db.delete_user(id) {
StatusCode::NO_CONTENT
} else {
StatusCode::NOT_FOUND
}
}
// ============================================
// Main
// ============================================
#[tokio::main]
async fn main() {
let db = Arc::new(Database::new());
let app = Router::new()
.route("/health", get(health))
.route("/api/users", get(get_users).post(create_user))
.route("/api/users/:id", get(get_user).delete(delete_user_handler))
.with_state(db);
let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();
println!("Server running on http://localhost:8080");
axum::serve(listener, app).await.unwrap();
}