use ruffus::{App, Request, Response};
use serde::{Deserialize, Serialize};
use http::StatusCode;
#[derive(Debug, Deserialize, Serialize)]
struct User {
id: Option<u32>,
name: String,
email: String,
age: Option<u32>,
}
#[derive(Debug, Deserialize)]
struct CreateUserRequest {
name: String,
email: String,
age: Option<u32>,
}
#[derive(Debug, Deserialize)]
struct UpdateUserRequest {
name: Option<String>,
email: Option<String>,
age: Option<u32>,
}
#[derive(Serialize)]
struct ApiResponse<T> {
success: bool,
data: Option<T>,
message: Option<String>,
}
impl<T> ApiResponse<T> {
fn success(data: T) -> Self {
Self {
success: true,
data: Some(data),
message: None,
}
}
fn error(message: String) -> Self {
Self {
success: false,
data: None,
message: Some(message),
}
}
}
#[tokio::main]
async fn main() {
let mut app = App::new();
app.get("/users", |_req: Request| async {
let users = vec![
User {
id: Some(1),
name: "Alice Johnson".to_string(),
email: "alice@example.com".to_string(),
age: Some(28),
},
User {
id: Some(2),
name: "Bob Smith".to_string(),
email: "bob@example.com".to_string(),
age: Some(35),
},
User {
id: Some(3),
name: "Charlie Brown".to_string(),
email: "charlie@example.com".to_string(),
age: None,
},
];
Response::json(&ApiResponse::success(users))
});
app.get("/users/:id", |req: Request| async move {
let id = req.param("id").unwrap_or("0");
let user_id: u32 = match id.parse() {
Ok(id) => id,
Err(_) => {
return Response::json(&ApiResponse::<()>::error(
"Invalid user ID".to_string()
)).map(|r| {
r.status(StatusCode::BAD_REQUEST)
});
}
};
let user = User {
id: Some(user_id),
name: "Alice Johnson".to_string(),
email: "alice@example.com".to_string(),
age: Some(28),
};
Response::json(&ApiResponse::success(user))
});
app.post("/users", |mut req: Request| async move {
let body: CreateUserRequest = match req.json().await {
Ok(body) => body,
Err(e) => {
return Response::json(&ApiResponse::<()>::error(
format!("Invalid JSON: {}", e)
)).map(|r| {
r.status(StatusCode::BAD_REQUEST)
});
}
};
if body.name.is_empty() {
return Response::json(&ApiResponse::<()>::error(
"Name cannot be empty".to_string()
)).map(|r| {
r.status(StatusCode::BAD_REQUEST)
});
}
if !body.email.contains('@') {
return Response::json(&ApiResponse::<()>::error(
"Invalid email address".to_string()
)).map(|r| {
r.status(StatusCode::BAD_REQUEST)
});
}
let user = User {
id: Some(123),
name: body.name,
email: body.email,
age: body.age,
};
Response::json(&ApiResponse::success(user)).map(|r| {
r.status(StatusCode::CREATED)
})
});
app.put("/users/:id", |mut req: Request| async move {
let id = req.param("id").unwrap_or("0");
let user_id: u32 = match id.parse() {
Ok(id) => id,
Err(_) => {
return Response::json(&ApiResponse::<()>::error(
"Invalid user ID".to_string()
)).map(|r| {
r.status(StatusCode::BAD_REQUEST)
});
}
};
let body: UpdateUserRequest = match req.json().await {
Ok(body) => body,
Err(e) => {
return Response::json(&ApiResponse::<()>::error(
format!("Invalid JSON: {}", e)
)).map(|r| {
r.status(StatusCode::BAD_REQUEST)
});
}
};
let user = User {
id: Some(user_id),
name: body.name.unwrap_or_else(|| "Alice Johnson".to_string()),
email: body.email.unwrap_or_else(|| "alice@example.com".to_string()),
age: body.age,
};
Response::json(&ApiResponse::success(user))
});
app.delete("/users/:id", |req: Request| async move {
let id = req.param("id").unwrap_or("0");
let user_id: u32 = match id.parse() {
Ok(id) => id,
Err(_) => {
return Response::json(&ApiResponse::<()>::error(
"Invalid user ID".to_string()
)).map(|r| {
r.status(StatusCode::BAD_REQUEST)
});
}
};
use serde_json::json;
Response::json(&json!({
"success": true,
"message": format!("User {} deleted successfully", user_id)
}))
});
println!("Starting Ruffus JSON API server...");
println!("Try these endpoints:");
println!(" GET http://localhost:3000/users");
println!(" GET http://localhost:3000/users/1");
println!(" POST http://localhost:3000/users");
println!(" Body: {{\"name\": \"John\", \"email\": \"john@example.com\", \"age\": 30}}");
println!(" PUT http://localhost:3000/users/1");
println!(" Body: {{\"name\": \"Jane\"}}");
println!(" DELETE http://localhost:3000/users/1");
app.listen("127.0.0.1:3000").await.unwrap();
}