From 83da336a3ff40bd5e026f84d882b3cf07934ece0 Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sun, 2 Jul 2023 15:16:47 -0700 Subject: [PATCH] Make it easier to add watches from a CLI tool. --- src/lib.rs | 10 ++-- src/util.rs | 16 +++++- src/watches/handlers.rs | 111 +++++++++++++++++++++++++--------------- src/watches/mod.rs | 15 ++++++ 4 files changed, 104 insertions(+), 48 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5a9185a..d658431 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,9 @@ pub use db::get_db_pool; pub use db_id::DbId; pub mod import_utils; +pub use users::User; +pub use watches::{ShowKind, Watch, WatchQuest}; + // everything else is private to the crate mod db; mod db_id; @@ -24,10 +27,7 @@ mod watches; // things we want in the crate namespace use optional_optional_user::OptionalOptionalUser; use templates::*; -use users::User; -use watches::{templates::*, ShowKind, Watch}; - -use crate::watches::handlers::get_watch; +use watches::templates::*; type AuthContext = axum_login::extractors::AuthContext>; @@ -42,7 +42,7 @@ pub async fn app(db_pool: sqlx::SqlitePool, session_secret: &[u8]) -> axum::Rout use login::{get_login, get_logout, post_login, post_logout}; use signup::{get_create_user, get_signup_success, post_create_user}; use watches::handlers::{ - get_add_new_watch, get_search_watch, get_watches, post_add_existing_watch, + get_add_new_watch, get_search_watch, get_watch, get_watches, post_add_existing_watch, post_add_new_watch, }; diff --git a/src/util.rs b/src/util.rs index dd16117..375a8f1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -20,7 +20,7 @@ pub fn validate_optional_length( } } -/// Serde deserialization decorator to map empty Strings to None, +/// Serde deserialization decorator to map empty Strings to None pub fn empty_string_as_none<'de, D, T>(de: D) -> Result, D::Error> where D: serde::Deserializer<'de>, @@ -35,3 +35,17 @@ where .map(Some), } } + +/// Convert a stringy number like "1999" to a 64-bit signed unix epoch-based +/// timestamp +pub fn year_to_epoch(year: Option<&str>) -> Option { + year? + .trim() + .parse::() + .map(|year| { + let years = (year - 1970) as f32; + let days = (years * 365.2425) as i64; + days * 24 * 60 * 60 + }) + .ok() +} diff --git a/src/watches/handlers.rs b/src/watches/handlers.rs index beb101f..f1fb141 100644 --- a/src/watches/handlers.rs +++ b/src/watches/handlers.rs @@ -7,7 +7,11 @@ use serde::Deserialize; use sqlx::{query, query_as, SqlitePool}; use super::templates::{AddNewWatchPage, GetWatchPage, SearchWatchesPage}; -use crate::{db_id::DbId, util::empty_string_as_none, AuthContext, MyWatchesPage, ShowKind, Watch}; +use crate::{ + db_id::DbId, + util::{empty_string_as_none, year_to_epoch}, + AuthContext, MyWatchesPage, ShowKind, Watch, WatchQuest, +}; //-************************************************************************ // Constants @@ -116,48 +120,26 @@ pub async fn post_add_new_watch( { let watch_id = DbId::new(); let witch_watch_id = DbId::new(); - let release_date = form.year.map(|year| match year.trim().parse::() { - Ok(year) => { - let years = (year - 1970) as i64; - let days = (years as f32 * 365.2425) as i64; - Some(days * 24 * 60 * 60) - } - Err(_) => None, - }); - let mut tx = pool - .begin() - .await - .map_err(|_| WatchAddErrorKind::UnknownDBError)?; - query(ADD_WATCH_QUERY) - .bind(watch_id) - .bind(&form.title) - .bind(form.kind) - .bind(release_date) - .bind(form.metadata_url) - .bind(user.id) - .execute(&mut tx) - .await - .map_err(|err| { - tracing::error!("Got error: {err}"); - WatchAddErrorKind::UnknownDBError - })?; + let release_date = year_to_epoch(form.year.as_deref()); + let watch = Watch { + id: watch_id, + title: form.title, + kind: form.kind, + metadata_url: form.metadata_url, + length: None, + release_date, + added_by: user.id, + }; + let quest = WatchQuest { + id: witch_watch_id, + user: user.id, + watch: watch_id, + is_public: !form.private, + already_watched: form.watched_already, + }; + + add_new_watch_impl(&pool, &watch, Some(quest)).await?; - query(ADD_WITCH_WATCH_QUERY) - .bind(witch_watch_id) - .bind(user.id) - .bind(watch_id) - .bind(!form.private) - .bind(form.watched_already) - .execute(&mut tx) - .await - .map_err(|err| { - tracing::error!("Got error: {err}"); - WatchAddErrorKind::UnknownDBError - })?; - tx.commit().await.map_err(|err| { - tracing::error!("Got error: {err}"); - WatchAddErrorKind::UnknownDBError - })?; let location = format!("/watch/{watch_id}"); Ok(Redirect::to(&location)) } @@ -166,6 +148,51 @@ pub async fn post_add_new_watch( } } +pub(crate) async fn add_new_watch_impl( + db_pool: &SqlitePool, + watch: &Watch, + quest: Option, +) -> Result<(), WatchAddError> { + let mut tx = db_pool + .begin() + .await + .map_err(|_| WatchAddErrorKind::UnknownDBError)?; + query(ADD_WATCH_QUERY) + .bind(watch.id) + .bind(&watch.title) + .bind(watch.kind) + .bind(watch.release_date) + .bind(&watch.metadata_url) + .bind(watch.added_by) + .execute(&mut tx) + .await + .map_err(|err| { + tracing::error!("Got error: {err}"); + WatchAddErrorKind::UnknownDBError + })?; + + if let Some(quest) = quest { + query(ADD_WITCH_WATCH_QUERY) + .bind(quest.id) + .bind(quest.user) + .bind(quest.watch) + .bind(quest.is_public) + .bind(quest.already_watched) + .execute(&mut tx) + .await + .map_err(|err| { + tracing::error!("Got error: {err}"); + WatchAddErrorKind::UnknownDBError + })?; + } + tx.commit().await.map_err(|err| { + tracing::error!("Got error: {err}"); + WatchAddErrorKind::UnknownDBError + })?; + + Ok(()) +} + /// Add a Watch to your watchlist by selecting it with a checkbox pub async fn post_add_existing_watch( _auth: AuthContext, diff --git a/src/watches/mod.rs b/src/watches/mod.rs index e170da3..c4a2a9a 100644 --- a/src/watches/mod.rs +++ b/src/watches/mod.rs @@ -52,6 +52,9 @@ impl From for ShowKind { } } +//-************************************************************************ +/// Something able to be watched. +//-************************************************************************ #[derive( Debug, Default, @@ -86,3 +89,15 @@ impl Watch { } } } + +//-************************************************************************ +/// Something a user wants to watch +//-************************************************************************ +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WatchQuest { + pub id: DbId, + pub user: DbId, + pub watch: DbId, + pub is_public: bool, + pub already_watched: bool, +}