more "search" fleshing out
This commit is contained in:
parent
0fbcfb9c8c
commit
78b77dbd30
3 changed files with 92 additions and 40 deletions
|
@ -3,22 +3,29 @@ use axum::{
|
|||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde::{de, Deserialize, Deserializer};
|
||||
use sqlx::{query_as, SqlitePool};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::templates::{GetSearchWatches, GetWatch};
|
||||
use crate::{AuthContext, GetWatches, ShowKind, User, Watch};
|
||||
use crate::{AuthContext, GetWatches, ShowKind, Watch};
|
||||
|
||||
//-************************************************************************
|
||||
// Constants
|
||||
//-************************************************************************
|
||||
|
||||
const GET_WATCHES_QUERY: &str =
|
||||
const GET_SAVED_WATCHES_QUERY: &str =
|
||||
"select * from watches left join witch_watch on $1 = witch_watch.witch and watches.id = witch_watch.watch";
|
||||
|
||||
const GET_WATCH_QUERY: &str = "select * from watches where id = $1";
|
||||
|
||||
const EMPTY_SEARCH_QUERY: SearchQuery = SearchQuery {
|
||||
title: None,
|
||||
kind: None,
|
||||
year: None,
|
||||
search: None,
|
||||
};
|
||||
|
||||
//-************************************************************************
|
||||
// Error types for Watch creation
|
||||
//-************************************************************************
|
||||
|
@ -46,28 +53,26 @@ impl IntoResponse for WatchAddError {
|
|||
// Types for receiving arguments from search queries
|
||||
//-************************************************************************
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize)]
|
||||
pub struct SimpleSearchQuery {
|
||||
search: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize)]
|
||||
pub struct FullSearchQuery {
|
||||
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)]
|
||||
pub struct SearchQuery {
|
||||
#[serde(default, deserialize_with = "empty_string_as_none")]
|
||||
pub search: Option<String>,
|
||||
#[serde(default, deserialize_with = "empty_string_as_none")]
|
||||
pub title: Option<String>,
|
||||
#[serde(default, deserialize_with = "empty_string_as_none")]
|
||||
pub kind: Option<String>,
|
||||
#[serde(default, deserialize_with = "empty_string_as_none")]
|
||||
pub year: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub enum SearchQuery {
|
||||
Full(FullSearchQuery),
|
||||
Simple(SimpleSearchQuery),
|
||||
}
|
||||
|
||||
impl Default for SearchQuery {
|
||||
fn default() -> Self {
|
||||
SearchQuery::Simple(SimpleSearchQuery::default())
|
||||
}
|
||||
// kinda the main form?
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Eq)]
|
||||
pub struct PostAddWatch {
|
||||
pub id: Option<String>, // maybe this already exists
|
||||
pub title: String,
|
||||
pub kind: Option<ShowKind>,
|
||||
pub release_date: Option<String>, // need a date-picker or something
|
||||
pub metadata_url: Option<String>,
|
||||
}
|
||||
|
||||
//-************************************************************************
|
||||
|
@ -75,7 +80,12 @@ impl Default for SearchQuery {
|
|||
//-************************************************************************
|
||||
|
||||
/// Add a Watch to your watchlist (side effects system-add if missing)
|
||||
pub async fn post_add_watch() -> impl IntoResponse {}
|
||||
pub async fn post_add_watch(
|
||||
auth: AuthContext,
|
||||
State(pool): State<SqlitePool>,
|
||||
Form(form): Form<PostAddWatch>,
|
||||
) -> impl IntoResponse {
|
||||
}
|
||||
|
||||
/// A single Watch
|
||||
pub async fn get_watch(
|
||||
|
@ -89,7 +99,7 @@ pub async fn get_watch(
|
|||
"".to_string()
|
||||
};
|
||||
let id = id.trim();
|
||||
|
||||
let id = Uuid::try_parse(id).unwrap_or_default();
|
||||
let watch: Option<Watch> = query_as(GET_WATCH_QUERY)
|
||||
.bind(id)
|
||||
.fetch_one(&pool)
|
||||
|
@ -106,7 +116,7 @@ pub async fn get_watch(
|
|||
pub async fn get_watches(auth: AuthContext, State(pool): State<SqlitePool>) -> impl IntoResponse {
|
||||
let user = auth.current_user;
|
||||
let watches: Vec<Watch> = if (user).is_some() {
|
||||
query_as(GET_WATCHES_QUERY)
|
||||
query_as(GET_SAVED_WATCHES_QUERY)
|
||||
.bind(user.as_ref().unwrap().id)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
|
@ -120,28 +130,47 @@ pub async fn get_watches(auth: AuthContext, State(pool): State<SqlitePool>) -> i
|
|||
|
||||
pub async fn get_search_watch(
|
||||
auth: AuthContext,
|
||||
State(_pool): State<SqlitePool>,
|
||||
search: Option<Query<SearchQuery>>,
|
||||
State(pool): State<SqlitePool>,
|
||||
search: Query<SearchQuery>,
|
||||
) -> impl IntoResponse {
|
||||
use SearchQuery::*;
|
||||
let search = match search {
|
||||
Some(Query(Simple(SimpleSearchQuery { search }))) => search,
|
||||
Some(Query(Full(q))) => {
|
||||
// obviously this is dumb
|
||||
format!("{q:?}")
|
||||
}
|
||||
None => "".to_owned(),
|
||||
};
|
||||
let search = search.trim().to_string();
|
||||
|
||||
let user = auth.current_user;
|
||||
|
||||
let search = if search.0 != EMPTY_SEARCH_QUERY {
|
||||
let s = search.0;
|
||||
format!("{s:?}")
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
// until tantivy search
|
||||
let watches: Vec<Watch> = query_as("select * from watches")
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
GetSearchWatches {
|
||||
watches: vec![],
|
||||
watches,
|
||||
user,
|
||||
search,
|
||||
}
|
||||
}
|
||||
|
||||
//-************************************************************************
|
||||
// helper fns
|
||||
//-************************************************************************
|
||||
|
||||
/// Serde deserialization decorator to map empty Strings to None,
|
||||
fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: std::str::FromStr,
|
||||
T::Err: std::fmt::Display,
|
||||
{
|
||||
let opt = Option::<String>::deserialize(de)?;
|
||||
match opt.as_deref() {
|
||||
None | Some("") => Ok(None),
|
||||
Some(s) => std::str::FromStr::from_str(s)
|
||||
.map_err(de::Error::custom)
|
||||
.map(Some),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use askama::Template;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{OptionalOptionalUser, User, Watch};
|
||||
use crate::{OptionalOptionalUser, ShowKind, User, Watch};
|
||||
|
||||
#[derive(Debug, Default, Template, Deserialize, Serialize, PartialEq, Eq, OptionalOptionalUser)]
|
||||
#[template(path = "get_watches.html")]
|
||||
|
|
|
@ -16,12 +16,35 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<p>
|
||||
<h2>Simple Search</h2>
|
||||
<div class="simplesearch">
|
||||
<form action="/search" enctype="application/x-www-form-urlencoded" method="get">
|
||||
<label for="search">Looking for something else to watch?</label>
|
||||
<input type="text" name="search" id="search"></br>
|
||||
<input type="submit" value="Let's go!">
|
||||
</div>
|
||||
<h2>Fussy Search</h2>
|
||||
<div class="fullsearch">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" name="title" id="title"></br>
|
||||
|
||||
<label for="year">Release Year</label>
|
||||
<input type="text" name="year" id="year"></br>
|
||||
|
||||
<label for="kind">Type</label>
|
||||
<select id="kind" name="kind">
|
||||
<option value="">Unknown</option>
|
||||
<option value="0">Movie</option>
|
||||
<option value="1">Series</option>
|
||||
<option value="2">Limited Series</option>
|
||||
<option value="3">Short</option>
|
||||
<option value="4">Other</option>
|
||||
</select>
|
||||
|
||||
<input type="submit" value="Let's go!">
|
||||
</form>
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue