|
use poem::{listener::TcpListener, middleware::Tracing, Route, Server}; |
|
use poem_openapi::{ |
|
payload::Json, |
|
param::Path, |
|
ApiResponse, Object, OpenApi, OpenApiService, |
|
}; |
|
use serde::{Deserialize, Serialize}; |
|
|
|
#[derive(Debug, Object, Serialize)] |
|
struct ErrorBody { |
|
code: String, |
|
message: String, |
|
} |
|
|
|
#[derive(Debug, ApiResponse)] |
|
enum ApiErr { |
|
#[oai(status = 400)] |
|
BadRequest(Json<ErrorBody>), |
|
|
|
#[oai(status = 404)] |
|
NotFound(Json<ErrorBody>), |
|
|
|
// #[oai(status = 500)] |
|
// Internal(Json<ErrorBody>), |
|
} |
|
|
|
fn bad_request(msg: impl Into<String>) -> ApiErr { |
|
ApiErr::BadRequest(Json(ErrorBody { code: "BAD_REQUEST".into(), message: msg.into() })) |
|
} |
|
fn not_found() -> ApiErr { |
|
ApiErr::NotFound(Json(ErrorBody { code: "NOT_FOUND".into(), message: "not found".into() })) |
|
} |
|
|
|
#[derive(Debug, Object, Serialize, Deserialize)] |
|
struct Health { |
|
ok: bool, |
|
} |
|
|
|
#[derive(Debug, Object, Serialize, Deserialize)] |
|
struct User { |
|
id: i32, |
|
name: String, |
|
} |
|
|
|
#[derive(Debug, Object, Deserialize)] |
|
struct CreateUser { |
|
name: String, |
|
} |
|
|
|
struct Api; |
|
|
|
#[OpenApi] |
|
impl Api { |
|
#[oai(path = "/health", method = "get")] |
|
async fn health(&self) -> Json<Health> { |
|
Json(Health { ok: true }) |
|
} |
|
|
|
#[oai(path = "/users/:id", method = "get")] |
|
async fn get_user(&self, id: Path<i32>) -> Result<Json<User>, ApiErr> { |
|
let id = id.0; |
|
if id == 1 { |
|
Ok(Json(User { id, name: "Alice".into() })) |
|
} else { |
|
Err(not_found()) |
|
} |
|
} |
|
|
|
#[oai(path = "/users", method = "post")] |
|
async fn create_user(&self, body: Json<CreateUser>) -> Result<Json<User>, ApiErr> { |
|
let name = body.0.name.trim().to_string(); |
|
if name.is_empty() { |
|
return Err(bad_request("name must not be empty")); |
|
} |
|
Ok(Json(User { id: 100, name })) |
|
} |
|
} |
|
|
|
#[tokio::main] |
|
async fn main() -> Result<(), std::io::Error> { |
|
tracing_subscriber::fmt::init(); |
|
|
|
let api_service = OpenApiService::new(Api, "Poem REST API", "0.1.0") |
|
.server("http://127.0.0.1:3000/api"); |
|
let ui = api_service.swagger_ui(); |
|
|
|
let app = Route::new() |
|
.nest("/api", api_service) |
|
.nest("/docs", ui) |
|
.with(Tracing); |
|
|
|
Server::new(TcpListener::bind("127.0.0.1:3000")) |
|
.run(app) |
|
.await |
|
} |