use std::net::Ipv4Addr;
use kynos::{
extract::{
body::{OneOf, binary::Binary, form::Form, multipart::MultipartForm, text::Text},
media::{MediaType, Png},
},
openapi::{Schema as OpenApiSchema, model::schema::types::SchemaType},
prelude::*,
schema::registry::Registry,
server::Server,
};
use serde::{Deserialize, Serialize};
struct Manifest;
impl MediaType for Manifest {
const MEDIA_TYPE: &'static str = "application/vnd.example.manifest+json";
}
#[derive(Serialize, Deserialize)]
struct Thumbnail(String);
impl kynos::schema::Schema for Thumbnail {
fn schema(_registry: &mut Registry) -> OpenApiSchema {
let mut schema = OpenApiSchema::of_type(SchemaType::String);
if let OpenApiSchema::Object(object) = &mut schema {
object.content_encoding = Some("base64".to_owned());
object.content_media_type = Some("image/png".to_owned());
object.max_length = Some(1_398_101);
}
schema
}
}
#[derive(Schema, Serialize, Deserialize)]
struct Product {
id: u64,
name: String,
thumbnail: Option<Thumbnail>,
}
#[derive(Schema, Serialize, Deserialize)]
struct ProductForm {
id: u64,
name: String,
}
#[allow(dead_code)]
#[derive(Schema, MultipartForm)]
struct Upload {
name: String,
#[schema(max_items = 8)]
images: Vec<kynos::extract::body::multipart::FilePart>,
}
#[kynos::post("/products")]
async fn create_product(body: OneOf<Json<Product>, Form<ProductForm>>) -> NoContent {
match body {
OneOf::Left(Json(product)) => println!("json: {}", product.name),
OneOf::Right(Form(form)) => println!("form: {}", form.name),
}
NoContent
}
#[kynos::post("/products/notes")]
async fn create_note(Text(note): Text) -> NoContent {
println!("note of {} bytes", note.len());
NoContent
}
#[kynos::post("/products/images")]
async fn upload_images(MultipartForm(upload): MultipartForm<Upload>) -> NoContent {
println!("upload: {}", upload.name);
for image in &upload.images {
println!("image of {} bytes", image.bytes.len());
}
NoContent
}
#[kynos::put("/products/{id}/image")]
async fn replace_image(Path(path): Path<ProductPath>, body: Binary<Png>) -> NoContent {
println!("{}: {} bytes of png", path.id, body.into_inner().len());
NoContent
}
#[kynos::post("/products/manifests")]
async fn upload_manifest(body: OneOf<Binary<Manifest>, Text>) -> NoContent {
match body {
OneOf::Left(manifest) => println!("vendor manifest: {} bytes", manifest.into_inner().len()),
OneOf::Right(Text(manifest)) => println!("plain manifest: {} bytes", manifest.len()),
}
NoContent
}
#[allow(dead_code)]
#[derive(Schema, PathParams)]
struct ProductPath {
id: u64,
}
#[tokio::main]
async fn main() -> kynos::Result<()> {
let router = Router::<()>::new().mount(kynos::routes![
create_product,
create_note,
upload_images,
replace_image,
upload_manifest,
]);
let document = router.openapi()?;
println!("{}", document.to_json()?);
Server::new(router.build(())?)
.bind((Ipv4Addr::UNSPECIFIED, 3000))
.serve()
.await
}