9 unstable releases (3 breaking)
new 0.3.3 | Jun 4, 2024 |
---|---|
0.3.2 | Jun 1, 2024 |
0.3.0 | May 25, 2024 |
0.2.1 | May 8, 2024 |
0.0.0-alpha | Nov 25, 2023 |
#1296 in Web programming
583 downloads per month
Used in hypers
3.5MB
6.5K
SLoC
⚡️ Quick Start
Cargo.toml
[dependencies]
hypers = { version = "0.8", features = ["full","openapi","scalar","rapidoc","redoc"] }
tokio = { version = "=1.37.0", features = ["full"] }
serde = { version = "=1.0.201", features = ["derive"] }
Rust Code
use hypers::{hyper::StatusCode, once_cell::sync::Lazy, prelude::*};
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
static STORE: Lazy<Db> = Lazy::new(new_store);
pub type Db = Mutex<Vec<Todo>>;
pub fn new_store() -> Db {
Mutex::new(Vec::new())
}
#[derive(Serialize, Deserialize, Clone, Debug, ToSchema)]
pub struct Todo {
#[hypers(schema(example = 1))]
pub id: u64,
#[hypers(schema(example = "Buy coffee"))]
pub text: String,
pub completed: bool,
}
struct Api;
#[openapi(name = "/api", tag = "todos")]
impl Api {
/// List todos.
#[get(
"/list_todos",
parameter(
("offset", description = "Offset is an query paramter."),
("limit", description = "Offset is an query paramter."),
)
)]
async fn list_todos(offset: Query<usize>, limit: Query<usize>) -> Json<Vec<Todo>> {
let todos = STORE.lock().await;
let todos: Vec<Todo> = todos
.clone()
.into_iter()
.skip(offset.0)
.take(limit.0)
.collect();
Json(todos)
}
/// Create new todo.
#[post("/create_todo", status(201, 409))]
async fn create_todo(req: Json<Todo>) -> Result<StatusCode, StatusError> {
let mut vec = STORE.lock().await;
for todo in vec.iter() {
if todo.id == req.id {
return Err(StatusError::bad_request().detail("todo already exists"));
}
}
vec.push(req.0);
Ok(StatusCode::CREATED)
}
// Currently, this type of routing is not supported
/// Update existing todo.
#[patch("/update_todo/:id", status(200, 404))]
async fn update_todo(id: Path<u64>, updated: Json<Todo>) -> Result<StatusCode, StatusError> {
let mut vec = STORE.lock().await;
for todo in vec.iter_mut() {
if todo.id == *id {
*todo = (*updated).clone();
return Ok(StatusCode::OK);
}
}
Err(StatusError::not_found())
}
// Currently, this type of routing is not supported
#[delete("/:id", status(200, 401, 404))]
async fn delete_todo(id: Path<u64>) -> Result<StatusCode, StatusError> {
let mut vec = STORE.lock().await;
let len = vec.len();
vec.retain(|todo| todo.id != *id);
let deleted = vec.len() != len;
if deleted {
Ok(StatusCode::NO_CONTENT)
} else {
Err(StatusError::not_found())
}
}
}
pub async fn index(_: Request) -> impl Responder {
Text::Html(INDEX_HTML)
}
#[tokio::main]
async fn main() -> Result<()> {
let mut root = Router::default();
root.get("/", index);
root.push(Api);
let openapi = OpenApi::new("todos api", "0.0.1");
root.openapi("/api-doc/openapi.json", openapi);
let swagger = SwaggerUi::new("/api-doc/openapi.json");
root.get("/swagger_ui/*", swagger); // http://127.0.0.1:7878/swagger_ui/
let rapidoc = RapiDoc::new("/api-doc/openapi.json");
root.get("/rapidoc", rapidoc); // http://127.0.0.1:7878/rapidoc
let redoc = ReDoc::new("/api-doc/openapi.json");
root.get("/redoc", redoc); // http://127.0.0.1:7878/redoc
let scalar: Scalar = Scalar::new("/api-doc/openapi.json");
root.get("/scalar", scalar); // http://127.0.0.1:7878/scalar
println!("root = {:#?}", root);
let listener = hypers::TcpListener::bind("127.0.0.1:7878").await?;
hypers::listen(root, listener).await
}
static INDEX_HTML: &str = r#"<!DOCTYPE html>
<html>
<head>
<title>Oapi todos</title>
</head>
<body>
<ul>
<li><a href="swagger_ui/" target="_blank">swagger_ui</a></li>
<li><a href="scalar" target="_blank">scalar</a></li>
<li><a href="rapidoc" target="_blank">rapidoc</a></li>
<li><a href="redoc" target="_blank">redoc</a></li>
</ul>
</body>
</html>
"#;
Dependencies
~6–17MB
~208K SLoC