#![deny(clippy::all)]
use axum::{
Router,
extract::Path,
http::StatusCode,
response::{IntoResponse, Response},
routing::patch,
};
use serde_json::json;
use std::net::SocketAddr;
use tokio::net::TcpListener;
const KNOWN_IDS: &[&str] = &["1", "42", "100"];
fn is_known(id: &str) -> bool {
KNOWN_IDS.contains(&id)
}
async fn resource(Path(id): Path<String>) -> Response {
if is_known(&id) {
StatusCode::UNPROCESSABLE_ENTITY.into_response()
} else {
StatusCode::NOT_FOUND.into_response()
}
}
async fn creating(Path(id): Path<String>) -> Response {
if is_known(&id) {
StatusCode::UNPROCESSABLE_ENTITY.into_response()
} else {
StatusCode::CREATED.into_response()
}
}
async fn success(Path(id): Path<String>) -> Response {
if is_known(&id) {
let body = json!({"id": id, "updated": true});
(StatusCode::OK, axum::Json(body)).into_response()
} else {
StatusCode::NOT_FOUND.into_response()
}
}
async fn no_content(Path(id): Path<String>) -> Response {
if is_known(&id) {
StatusCode::NO_CONTENT.into_response()
} else {
StatusCode::NOT_FOUND.into_response()
}
}
async fn conflict(Path(id): Path<String>) -> Response {
if is_known(&id) {
let body = json!({"error": "conflict", "detail": "concurrent modification"});
(StatusCode::CONFLICT, axum::Json(body)).into_response()
} else {
StatusCode::NOT_FOUND.into_response()
}
}
async fn media(Path(id): Path<String>) -> Response {
if is_known(&id) {
StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response()
} else {
StatusCode::NOT_FOUND.into_response()
}
}
async fn precondition(Path(id): Path<String>) -> Response {
if is_known(&id) {
StatusCode::PRECONDITION_FAILED.into_response()
} else {
StatusCode::NOT_FOUND.into_response()
}
}
async fn normalized(_id: Path<String>) -> Response {
StatusCode::NOT_FOUND.into_response()
}
fn router() -> Router {
Router::new()
.route("/resource/{id}", patch(resource))
.route("/creating/{id}", patch(creating))
.route("/success/{id}", patch(success))
.route("/no-content/{id}", patch(no_content))
.route("/conflict/{id}", patch(conflict))
.route("/media/{id}", patch(media))
.route("/precondition/{id}", patch(precondition))
.route("/normalized/{id}", patch(normalized))
}
pub async fn spawn() -> SocketAddr {
let listener = TcpListener::bind("127.0.0.1:0")
.await
.expect("failed to bind test server listener");
let addr = listener
.local_addr()
.expect("failed to read local_addr from listener");
tokio::spawn(async move {
axum::serve(listener, router())
.await
.expect("test server failed");
});
addr
}