use chrono::offset::Utc; use rocket::request::Form; use crate::routes::Page; use crate::template_utils::{IntoContext, Ructe}; use plume_models::{search::Query, PlumeRocket}; use std::str::FromStr; #[derive(Default, FromForm)] pub struct SearchQuery { q: Option, title: Option, subtitle: Option, content: Option, instance: Option, author: Option, tag: Option, blog: Option, lang: Option, license: Option, after: Option, before: Option, page: Option, } macro_rules! param_to_query { ( $query:ident, $parsed_query:ident; normal: $($field:ident),*; date: $($date:ident),*) => { $( let mut rest = $query.$field.as_ref().map(String::as_str).unwrap_or_default(); while !rest.is_empty() { let (token, r) = Query::get_first_token(rest); rest = r; $parsed_query.$field(token, None); } )* $( if let Some(ref field) = $query.$date { let mut rest = field.as_str(); while !rest.is_empty() { use chrono::naive::NaiveDate; let (token, r) = Query::get_first_token(rest); rest = r; if let Ok(token) = NaiveDate::parse_from_str(token, "%Y-%m-%d") { $parsed_query.$date(&token); } } } )* } } #[get("/search?")] pub fn search(query: Option>, rockets: PlumeRocket) -> Ructe { let conn = &*rockets.conn; let query = query.map(Form::into_inner).unwrap_or_default(); let page = query.page.unwrap_or_default(); let mut parsed_query = Query::from_str(&query.q.as_deref().unwrap_or_default()).unwrap_or_default(); param_to_query!(query, parsed_query; normal: title, subtitle, content, tag, instance, author, blog, lang, license; date: before, after); let str_query = parsed_query.to_string(); if str_query.is_empty() { render!(search::index( &rockets.to_context(), &format!("{}", Utc::today().format("%Y-%m-d")) )) } else { let res = rockets .searcher .search_document(&conn, parsed_query, page.limits()); let next_page = if res.is_empty() { 0 } else { page.0 + 1 }; render!(search::result( &rockets.to_context(), &str_query, res, page.0, next_page )) } }