diff --git a/core/src/requests/tabs.rs b/core/src/requests/tabs.rs index 61d58f1..a7a91cc 100644 --- a/core/src/requests/tabs.rs +++ b/core/src/requests/tabs.rs @@ -2,17 +2,15 @@ use eva::{data, int}; use crate::{ errors, - types::{True, author, entity, game, tab}, + types::{True, author, entity, game, tab, user}, }; pub mod list { use super::*; #[data] - #[derive(Default)] pub struct Args { - #[serde(default)] - pub resolve_marks: bool, + pub user: user::Id, } #[data] @@ -27,7 +25,10 @@ pub mod list { } #[data(error, display("_"))] - pub enum Err {} + pub enum Err { + #[display("{_0}")] + NotFound(#[from] errors::users::NotFound), + } } pub mod list_items { @@ -45,6 +46,7 @@ pub mod list_items { #[data] pub struct Args { pub tab: tab::Id, + pub user: user::Id, pub start_from: Option, #[serde(default)] pub limit: Limit, @@ -76,11 +78,13 @@ pub mod insert { #[data] pub struct Args { - pub id: tab::Id, + pub user: user::Id, + pub tab: tab::Id, pub item: entity::Id, } - pub type Ok = True; + #[data] + pub struct Ok(pub True); #[data(error)] pub enum Err { @@ -90,6 +94,8 @@ pub mod insert { NoSuchTab(#[from] errors::tabs::NoSuchTab), #[display("{_0}")] InvalidItemId(#[from] errors::tabs::InvalidItemId), + #[display("{_0}")] + Duplicate(#[from] errors::tabs::Duplicate), } } @@ -98,7 +104,8 @@ pub mod delete { #[data] pub struct Args { - pub id: tab::Id, + pub user: user::Id, + pub tab: tab::Id, pub item: entity::Id, } diff --git a/http/src/client/authors.rs b/http/src/client/authors.rs index ce22b0b..24a880d 100644 --- a/http/src/client/authors.rs +++ b/http/src/client/authors.rs @@ -7,7 +7,7 @@ use crate::requests::authors as requests; impl Authors for HttpClient { fn get(&mut self) -> impl CallStep { self.do_call(Method::GET, |get::Args { author }| { - (format_compact!("/authors/{author}"), requests::Get {}) + (c!("/authors/{author}"), requests::Get {}) }) } @@ -21,7 +21,7 @@ impl Authors for HttpClient { owner, }| { ( - "/authors".into(), + c!("/authors"), requests::Create { title, description, @@ -36,7 +36,7 @@ impl Authors for HttpClient { fn update(&mut self) -> impl CallStep { self.do_call(Method::PATCH, |update::Args { author, update }| { ( - format_compact!("/authors/{author}"), + c!("/authors/{author}"), requests::Update { title: update.title, description: update.description, diff --git a/http/src/client/games.rs b/http/src/client/games.rs index e3723ed..6258cc6 100644 --- a/http/src/client/games.rs +++ b/http/src/client/games.rs @@ -9,13 +9,22 @@ use crate::requests::games as requests; impl Games for HttpClient { fn get(&mut self) -> impl CallStep { - self.do_call(Method::GET, |get::Args { game }| match game { - Selector::Id(id) => (format_compact!("/games/{}", id.to_str()), requests::Get {}), - Selector::FullyQualified(fq) => ( - format_compact!("/games/{}/{}", fq.author, fq.slug), - requests::Get {}, - ), - }) + self.do_call( + Method::GET, + |get::Args { + game, + resolve_marks, + }| match game { + Selector::Id(id) => ( + c!("/games/{}", id.to_str()), + requests::Get { resolve_marks }, + ), + Selector::FullyQualified(fq) => ( + c!("/games/{}/{}", fq.author, fq.slug), + requests::Get { resolve_marks }, + ), + }, + ) } fn search(&mut self) -> impl CallStep { @@ -81,7 +90,7 @@ impl Games for HttpClient { fn update(&mut self) -> impl CallStep { self.do_call(Method::PATCH, |update::Args { id, update }| { ( - format_compact!("/games/{id}"), + c!("/games/{id}"), requests::Update { title: update.title, description: update.description, diff --git a/http/src/client/mod.rs b/http/src/client/mod.rs index f4ecdca..5ff99fc 100644 --- a/http/src/client/mod.rs +++ b/http/src/client/mod.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use eva::{ error::ShitHappens, - str::{CompactString, format_compact}, + str::{CompactString, format_compact as c}, }; use http::Method; @@ -17,6 +17,7 @@ use viendesu_core::{ games::Games, marks::{Badges, Genres, Tags}, messages::Messages, + tabs::Tabs, threads::Threads, users::Users, }, @@ -36,6 +37,7 @@ mod games; mod users; mod marks; +mod tabs; struct DoRequest<'c, P> { client: &'c mut HttpClient, @@ -116,11 +118,11 @@ impl HttpClient { .client .execute(req.build().expect("shit happens")) .await - .map_err(|e| Aux::InternalError(format_compact!("failed to make request: {e:#}")))?; + .map_err(|e| Aux::InternalError(c!("failed to make request: {e:#}")))?; let bytes = response .bytes() .await - .map_err(|e| Aux::InternalError(format_compact!("failed to read bytes: {e:#}")))?; + .map_err(|e| Aux::InternalError(c!("failed to read bytes: {e:#}")))?; #[derive(serde::Deserialize)] #[serde(untagged)] diff --git a/http/src/client/tabs.rs b/http/src/client/tabs.rs new file mode 100644 index 0000000..2d67ee3 --- /dev/null +++ b/http/src/client/tabs.rs @@ -0,0 +1,49 @@ +use super::*; + +use crate::requests::tabs; + +use viendesu_core::requests::tabs::{delete, insert, list, list_items}; + +impl Tabs for HttpClient { + fn list(&mut self) -> impl CallStep { + self.do_call(Method::GET, |list::Args { user }| { + (c!("/users/{user}/tabs"), tabs::List {}) + }) + } + + fn list_items( + &mut self, + ) -> impl CallStep { + self.do_call( + Method::GET, + |list_items::Args { + tab, + user, + start_from, + limit, + resolve_marks, + }| { + ( + c!("/users/{user}/tabs/{tab}"), + tabs::ListItems { + resolve_marks, + start_from, + limit, + }, + ) + }, + ) + } + + fn insert(&mut self) -> impl CallStep { + self.do_call(Method::POST, |insert::Args { user, tab, item }| { + (c!("/users/{user}/tabs/{tab}"), tabs::Insert { item }) + }) + } + + fn delete(&mut self) -> impl CallStep { + self.do_call(Method::DELETE, |delete::Args { user, tab, item }| { + (c!("/users/{user}/tabs/{tab}/{item}"), tabs::Delete {}) + }) + } +} diff --git a/http/src/client/users.rs b/http/src/client/users.rs index 6a2fab2..9e41a60 100644 --- a/http/src/client/users.rs +++ b/http/src/client/users.rs @@ -1,7 +1,5 @@ use super::*; -use eva::str::format_compact; - use viendesu_core::requests::users::{ begin_auth, check_auth, confirm_sign_up, finish_auth, get, sign_in, sign_up, update, }; @@ -11,7 +9,7 @@ use crate::requests::users as requests; impl Users for HttpClient { fn get(&mut self) -> impl CallStep { self.do_call(Method::GET, |get::Args { user }| match user { - Some(u) => (format_compact!("/users/{u}"), requests::Get {}), + Some(u) => (c!("/users/{u}"), requests::Get {}), None => ("/users/me".into(), requests::Get {}), }) } @@ -37,7 +35,7 @@ impl Users for HttpClient { ) -> impl CallStep { self.do_call(Method::POST, |finish_auth::Args { auth_session }| { ( - format_compact!("/users/finish-auth/{auth_session}"), + c!("/users/finish-auth/{auth_session}"), requests::FinishAuth {}, ) }) @@ -52,10 +50,7 @@ impl Users for HttpClient { fn update(&mut self) -> impl CallStep { self.do_call(Method::PATCH, |update::Args { user, update }| match user { - Some(u) => ( - format_compact!("/users/{u}"), - requests::Update::from(update), - ), + Some(u) => (c!("/users/{u}"), requests::Update::from(update)), None => ("/users/me".into(), requests::Update::from(update)), }) } diff --git a/http/src/requests/games.rs b/http/src/requests/games.rs index 82cc1ca..474afc7 100644 --- a/http/src/requests/games.rs +++ b/http/src/requests/games.rs @@ -48,7 +48,10 @@ status_code::direct!(reqs::search::Ok => OK); status_code::map!(reqs::search::Err => [NoSuchAuthor]); #[data] -pub struct Get {} +pub struct Get { + #[serde(default)] + pub resolve_marks: bool, +} impl_req!(Get => [reqs::get::Ok; reqs::get::Err]); diff --git a/http/src/requests/mod.rs b/http/src/requests/mod.rs index 5c1f75a..8bd9d12 100644 --- a/http/src/requests/mod.rs +++ b/http/src/requests/mod.rs @@ -25,6 +25,7 @@ macro_rules! impl_req { } pub mod marks; +pub mod tabs; pub mod users; pub mod authors; diff --git a/http/src/requests/tabs.rs b/http/src/requests/tabs.rs new file mode 100644 index 0000000..593c05c --- /dev/null +++ b/http/src/requests/tabs.rs @@ -0,0 +1,53 @@ +use eva::data; + +use viendesu_core::{errors, requests::tabs as reqs, types::entity}; + +use crate::requests::status_code; + +#[data] +pub struct Delete {} + +impl_req!(Delete => [reqs::delete::Ok; reqs::delete::Err]); +status_code::direct!(reqs::delete::Ok => OK); +status_code::map!(reqs::delete::Err => [InvalidItemId, NoSuchItem, NoSuchTab]); + +#[data] +pub struct Insert { + pub item: entity::Id, +} + +impl_req!(Insert => [reqs::insert::Ok; reqs::insert::Err]); +status_code::direct!(reqs::insert::Ok => CREATED); +status_code::map!(reqs::insert::Err => [Duplicate, InvalidKind, NoSuchTab, InvalidItemId]); + +#[data] +pub struct ListItems { + #[serde(default)] + pub resolve_marks: bool, + #[serde(default)] + pub start_from: Option, + #[serde(default)] + pub limit: reqs::list_items::Limit, +} + +impl_req!(ListItems => [reqs::list_items::Ok; reqs::list_items::Err]); +status_code::direct!(reqs::list_items::Ok => OK); +status_code::map!(reqs::list_items::Err => [InvalidItemId, NoSuchTab]); + +#[data] +pub struct List {} + +impl_req!(List => [reqs::list::Ok; reqs::list::Err]); +status_code::direct!(reqs::list::Ok => OK); +status_code::map!(reqs::list::Err => [NotFound]); + +const _: () = { + use errors::tabs::*; + use status_code::direct; + + direct!(InvalidKind => BAD_REQUEST); + direct!(Duplicate => BAD_REQUEST); + direct!(InvalidItemId => BAD_REQUEST); + direct!(NoSuchTab => NOT_FOUND); + direct!(NoSuchItem => NOT_FOUND); +}; diff --git a/http/src/server/handler.rs b/http/src/server/handler.rs index 2a5e220..78c3493 100644 --- a/http/src/server/handler.rs +++ b/http/src/server/handler.rs @@ -169,6 +169,15 @@ where Handler::patch(load_args::).exec(make_request) } +pub fn delete(make_request: M) -> FinishedHandler> +where + R: ServerRequest, + T: Types, + M: MakeRequest, +{ + Handler::delete(load_args::).exec(make_request) +} + pub struct Handler(Inner); impl> Handler { @@ -196,6 +205,14 @@ impl> Handler { }) } + pub fn delete(make_context: Cx) -> Handler { + Self(Inner { + make_context, + method: MethodFilter::DELETE, + _phantom: PhantomData, + }) + } + pub fn exec(self, make_request: M) -> FinishedHandler where T: Types, diff --git a/http/src/server/routes.rs b/http/src/server/routes.rs index 1463c53..1cb032d 100644 --- a/http/src/server/routes.rs +++ b/http/src/server/routes.rs @@ -1,7 +1,7 @@ use crate::server::{ Types, context::Context as Ctx, - handler::{RouterScope, get, patch, post}, + handler::{RouterScope, delete, get, patch, post}, }; use viendesu_core::{ @@ -113,7 +113,7 @@ fn users(router: RouterScope) -> RouterScope { requests::users::{ begin_auth, check_auth, confirm_sign_up, finish_auth, get, sign_in, sign_up, update, }, - service::users::Users, + service::{tabs::Tabs, users::Users}, }; fn convert_update(u: Update) -> update::Update { @@ -253,6 +253,72 @@ fn users(router: RouterScope) -> RouterScope { }, ), ) + .nest("/{user}/tabs", |router| { + use crate::requests::tabs::{Delete, Insert, List, ListItems}; + use viendesu_core::requests::tabs::{delete, insert, list, list_items}; + + router + .route( + "/", + get(async |mut session: SessionOf, mut ctx: Ctx| { + let user = ctx.path().await?; + let List {} = ctx.request; + session.tabs().list().call(list::Args { user }).await + }), + ) + .nest("/{tab}", |router| { + router + .route( + "/{item}", + delete(async |mut session: SessionOf, mut ctx: Ctx| { + let (user, tab, item) = ctx.path().await?; + let Delete {} = ctx.request; + + session + .tabs() + .delete() + .call(delete::Args { user, tab, item }) + .await + }), + ) + .route( + "/", + post(async |mut session: SessionOf, mut ctx: Ctx| { + let (user, tab) = ctx.path().await?; + let Insert { item } = ctx.request; + + session + .tabs() + .insert() + .call(insert::Args { user, tab, item }) + .await + }), + ) + .route( + "/", + get(async |mut session: SessionOf, mut ctx: Ctx| { + let (user, tab) = ctx.path().await?; + let ListItems { + resolve_marks, + start_from, + limit, + } = ctx.request; + + session + .tabs() + .list_items() + .call(list_items::Args { + tab, + user, + start_from, + limit, + resolve_marks, + }) + .await + }), + ) + }) + }) } fn uploads(router: RouterScope) -> RouterScope { @@ -378,13 +444,14 @@ fn games(router: RouterScope) -> RouterScope { "/{game_id}", get(async |mut session: SessionOf, mut ctx: Ctx| { let game_id: game::Id = ctx.path().await?; - let Get {} = ctx.request; + let Get { resolve_marks } = ctx.request; session .games() .get() .call(get::Args { game: game_id.into(), + resolve_marks, }) .await }), @@ -393,11 +460,13 @@ fn games(router: RouterScope) -> RouterScope { "/{author}/{slug}", get(async |mut session: SessionOf, mut ctx: Ctx| { let (author, slug) = ctx.path::<(author::Selector, game::Slug)>().await?; + let Get { resolve_marks } = ctx.request; session .games() .get() .call(get::Args { + resolve_marks, game: game::Selector::FullyQualified(game::FullyQualified { author, slug }), }) .await