use log::{error, info}; use rouille::router; use rouille::Request; use rouille::Response; use std::error::Error; use crate::elastic_query::ElasticQuery; use crate::options; pub fn serve_request(req: &Request) -> Response { router!(req, (GET) (/search) => { query(req) }, _ => Response::empty_404() ) } fn query(req: &Request) -> Response { info!("Handling request: {:?}", req); let Some(query) = req.get_param("q") else { return Response::text("Query Parameter q must be specified!").with_status_code(400); }; match query.len() { 0..=options::query::MIN_LENGTH => { return Response::text(format!( "Min query length: {:?}", options::query::MIN_LENGTH )) .with_status_code(400); } options::query::MAX_LENGTH..=usize::MAX => { return Response::text(format!( "Max query length: {:?}", options::query::MAX_LENGTH )) .with_status_code(400); } _ => (), } let response = query_elastic_search(query); if let Err(e) = response { error!("Error searching: {}", e); return Response::text("No fitting awesome list found :(").with_status_code(404); } let response = response.unwrap(); let Some(name) = response.split('.').next() else { error!("Error getting name from response!"); return Response::text("No fitting awesome list found :(").with_status_code(404); }; info!("Redirecting to: /{}", name); Response::redirect_303(format!("/{}", name)) } fn query_elastic_search(query: String) -> Result> { let query = ElasticQuery::new(options::elastic_search::FIELDS.clone(), query); let res = query.send(options::elastic_search::URL.clone())?; Ok(res.hits.hits[0].source.file.file_name.clone()) } #[cfg(test)] mod test { const SEARCH_URL: &str = "/search"; use super::*; mod serve_request { use super::*; #[test] fn test_query_route_exists() { let req = Request::fake_http("GET", SEARCH_URL, vec![], vec![]); let res = serve_request(&req); assert_ne!(res.status_code, 404) } #[test] fn test_non_existant_route() { let req = Request::fake_http("GET", "/test", vec![], vec![]); let res = serve_request(&req); assert_eq!(res.status_code, 404) } } mod query { use super::*; #[test] fn no_query_parameter() { let req = Request::fake_http("GET", SEARCH_URL, vec![], vec![]); let res = query(&req); assert_eq!(res.status_code, 400) } #[test] fn query_too_long() { let q: String = std::iter::repeat("a") .take(options::query::MAX_LENGTH + 1) .collect(); let req = Request::fake_http("GET", format!("/{}?q={}", SEARCH_URL, q), vec![], vec![]); let res = query(&req); let mut body = String::new(); let _ = res.data.into_reader_and_size().0.read_to_string(&mut body); assert_eq!(body, "Max query length: 100"); assert_eq!(res.status_code, 400) } #[test] fn query_too_short() { let req = Request::fake_http("GET", format!("/{}?q=", SEARCH_URL), vec![], vec![]); let res = query(&req); assert_eq!(res.status_code, 400); let mut body = String::new(); let _ = res.data.into_reader_and_size().0.read_to_string(&mut body); assert_eq!(body, "Min query length: 3") } } }