use axum::{Json, Router, routing::post};
use axum_params::{Error, Params, UploadFile};
use futures_util::future;
use serde::{Deserialize, Serialize};
use tokio::io::AsyncReadExt;
#[derive(Debug, Serialize, Deserialize)]
struct CreatePostParams {
title: String,
content: String,
tags: Vec<String>,
cover: UploadFile, attachments: Vec<Attachment>, }
#[derive(Debug, Serialize, Deserialize)]
struct Attachment {
file: UploadFile,
name: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct AttachmentResponse {
name: String,
content_type: String,
content: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct UploadFileResponse {
name: String,
content_type: String,
content: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct CreatePostResponse {
title: String,
content: String,
tags: Vec<String>,
cover: UploadFileResponse,
attachments: Vec<AttachmentResponse>,
}
#[axum::debug_handler]
async fn create_post_handler(
Params(post, _): Params<CreatePostParams>,
) -> Result<Json<CreatePostResponse>, Error> {
let mut cover_file = post.cover.open().await.unwrap();
let mut cover_content = String::new();
cover_file.read_to_string(&mut cover_content).await.unwrap();
let attachments = future::join_all(post.attachments.into_iter().map(|a| async {
let mut file = a.file.open().await.unwrap();
let mut content = String::new();
file.read_to_string(&mut content).await.unwrap();
AttachmentResponse {
name: a.name,
content_type: a.file.content_type,
content,
}
}))
.await;
Ok(Json(CreatePostResponse {
title: post.title,
content: post.content,
tags: post.tags,
cover: UploadFileResponse {
name: post.cover.name,
content_type: post.cover.content_type,
content: cover_content,
},
attachments,
}))
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/posts", post(create_post_handler));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
println!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
}