138 lines
4.1 KiB
Rust
138 lines
4.1 KiB
Rust
/// These are our API handlers, the ends of each filter chain.
|
|
/// Notice how thanks to using `Filter::and`, we can define a function
|
|
/// with the exact arguments we'd expect from each filter in the chain.
|
|
/// No tuples are needed, it's auto flattened for the functions.
|
|
use anyhow::anyhow;
|
|
use http_api_problem::HttpApiProblem;
|
|
use std::convert::Infallible;
|
|
use tracing::debug;
|
|
use warp::http::StatusCode;
|
|
|
|
use crate::models::{Environment, ListOptions, Todo};
|
|
use crate::problem::reject_anyhow;
|
|
|
|
pub async fn list_todos(
|
|
opts: ListOptions,
|
|
env: Environment,
|
|
) -> Result<impl warp::Reply, warp::Rejection> {
|
|
env.caches
|
|
.list_todos
|
|
.get_response(opts.clone(), || async {
|
|
// Just return a JSON array of todos, applying the limit and offset.
|
|
let todos = env.db.lock().await;
|
|
let todos: Vec<Todo> = todos
|
|
.clone()
|
|
.into_iter()
|
|
.skip(opts.offset.unwrap_or(0))
|
|
.take(opts.limit.unwrap_or(std::usize::MAX))
|
|
.collect();
|
|
Ok(warp::reply::json(&todos))
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn get_todo(id: u64, env: Environment) -> Result<impl warp::Reply, warp::Rejection> {
|
|
env.caches
|
|
.todos
|
|
.get_response(id, || async {
|
|
debug!("get_todo: id={}", id);
|
|
let mut vec = env.db.lock().await;
|
|
|
|
// Look for the specified Todo...
|
|
for todo in vec.iter_mut() {
|
|
if todo.id == id {
|
|
return Ok(warp::reply::json(&todo));
|
|
}
|
|
}
|
|
|
|
debug!(" -> todo id not found!");
|
|
|
|
// If the for loop didn't return OK, then the ID doesn't exist...
|
|
Err(anyhow!(HttpApiProblem::new(format!(
|
|
"Todo {} not found",
|
|
id
|
|
))
|
|
.set_status(StatusCode::NOT_FOUND)))
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn create_todo(create: Todo, env: Environment) -> Result<impl warp::Reply, Infallible> {
|
|
debug!("create_todo: {:?}", create);
|
|
|
|
let mut vec = env.db.lock().await;
|
|
|
|
for todo in vec.iter() {
|
|
if todo.id == create.id {
|
|
debug!(" -> id already exists: {}", create.id);
|
|
// Todo with id already exists, return `400 BadRequest`.
|
|
return Ok(StatusCode::BAD_REQUEST);
|
|
}
|
|
}
|
|
|
|
// No existing Todo with id, so insert and return `201 Created`.
|
|
vec.push(create);
|
|
|
|
env.caches.list_todos.clear().await;
|
|
Ok(StatusCode::CREATED)
|
|
}
|
|
|
|
pub async fn update_todo(
|
|
id: u64,
|
|
update: Todo,
|
|
env: Environment,
|
|
) -> Result<impl warp::Reply, warp::Rejection> {
|
|
debug!("update_todo: id={}, todo={:?}", id, update);
|
|
let mut vec = env.db.lock().await;
|
|
|
|
// Look for the specified Todo...
|
|
for todo in vec.iter_mut() {
|
|
if todo.id == id {
|
|
*todo = update;
|
|
env.caches
|
|
.todos
|
|
.delete_response(id)
|
|
.await
|
|
.map_err(reject_anyhow)?;
|
|
env.caches.list_todos.clear().await;
|
|
return Ok(StatusCode::OK);
|
|
}
|
|
}
|
|
|
|
debug!(" -> todo id not found!");
|
|
|
|
// If the for loop didn't return OK, then the ID doesn't exist...
|
|
Ok(StatusCode::NOT_FOUND)
|
|
}
|
|
|
|
pub async fn delete_todo(id: u64, env: Environment) -> Result<impl warp::Reply, warp::Rejection> {
|
|
debug!("delete_todo: id={}", id);
|
|
|
|
let mut vec = env.db.lock().await;
|
|
|
|
let len = vec.len();
|
|
vec.retain(|todo| {
|
|
// Retain all Todos that aren't this id...
|
|
// In other words, remove all that *are* this id...
|
|
todo.id != id
|
|
});
|
|
|
|
// If the vec is smaller, we found and deleted a Todo!
|
|
let deleted = vec.len() != len;
|
|
|
|
if deleted {
|
|
env.caches
|
|
.todos
|
|
.delete_response(id)
|
|
.await
|
|
.map_err(reject_anyhow)?;
|
|
env.caches.list_todos.clear().await;
|
|
// respond with a `204 No Content`, which means successful,
|
|
// yet no body expected...
|
|
Ok(StatusCode::NO_CONTENT)
|
|
} else {
|
|
debug!(" -> todo id not found!");
|
|
Ok(StatusCode::NOT_FOUND)
|
|
}
|
|
}
|