Make it easier to add watches from a CLI tool.

This commit is contained in:
Joe Ardent 2023-07-02 15:16:47 -07:00
parent 04549fd7a5
commit 83da336a3f
4 changed files with 104 additions and 48 deletions

View file

@ -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<DbId, User, axum_login::SqliteStore<User>>;
@ -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,
};

View file

@ -20,7 +20,7 @@ pub fn validate_optional_length<E: Error>(
}
}
/// 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<Option<T>, 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<i64> {
year?
.trim()
.parse::<i32>()
.map(|year| {
let years = (year - 1970) as f32;
let days = (years * 365.2425) as i64;
days * 24 * 60 * 60
})
.ok()
}

View file

@ -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::<i32>() {
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<WatchQuest>,
) -> 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,

View file

@ -52,6 +52,9 @@ impl From<i64> 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,
}