diff --git a/src/config.rs b/src/config.rs index d7cf3b1..7addc5c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ use figment::{providers::Serialized, Figment}; use serde::{Deserialize, Serialize}; /// Struct holding config Options -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct Config { /// It stores the parsed port number option on which the server should launch. pub port: u16, @@ -21,7 +21,7 @@ pub struct Config { /// It toggles whether to use adaptive HTTP windows pub adaptive_window: bool, /// It stores all the engine names that were enabled by the user. - pub upstream_search_engines: Vec, + pub upstream_search_engines: crate::engines::Engines, /// It stores the time (secs) which controls the server request timeout. pub request_timeout: u8, /// Set the keep-alive time for client connections to the HTTP server @@ -67,15 +67,7 @@ impl Default for Config { logging: true, debug: false, adaptive_window: false, - upstream_search_engines: vec![ - "bing".into(), - "brave".into(), - "duckduckgo".into(), - "librex".into(), - "mojeek".into(), - "searx".into(), - "startpage".into(), - ], + upstream_search_engines: Default::default(), request_timeout: 2, tcp_connection_keep_alive: 10, pool_idle_connection_timeout: 30, diff --git a/src/engines/bing.rs b/src/engines/bing.rs index cdfb884..8465dbc 100644 --- a/src/engines/bing.rs +++ b/src/engines/bing.rs @@ -24,18 +24,19 @@ pub struct Bing { parser: SearchResultParser, } -impl Bing { +impl Default for Bing { /// Creates the Bing parser. - pub fn new() -> Result { - Ok(Self { + fn default() -> Self { + Self { parser: SearchResultParser::new( ".b_results", ".b_algo", "h2 a", ".tpcn a.tilk", ".b_caption p", - )?, - }) + ) + .expect("somehow you changed the static stings in the binary i guess"), + } } } diff --git a/src/engines/brave.rs b/src/engines/brave.rs index 771ac96..b816f1d 100644 --- a/src/engines/brave.rs +++ b/src/engines/brave.rs @@ -20,18 +20,19 @@ pub struct Brave { parser: SearchResultParser, } -impl Brave { +impl Default for Brave { /// Creates the Brave parser. - pub fn new() -> Result { - Ok(Self { + fn default() -> Self { + Self { parser: SearchResultParser::new( "#results h4", "#results [data-pos]", "a > .url", "a", ".snippet-description", - )?, - }) + ) + .expect("somehow you changed the static stings in the binary i guess"), + } } } diff --git a/src/engines/duckduckgo.rs b/src/engines/duckduckgo.rs index f6743a1..71e5ef5 100644 --- a/src/engines/duckduckgo.rs +++ b/src/engines/duckduckgo.rs @@ -23,18 +23,19 @@ pub struct DuckDuckGo { parser: SearchResultParser, } -impl DuckDuckGo { +impl Default for DuckDuckGo { /// Creates the DuckDuckGo parser. - pub fn new() -> Result { - Ok(Self { + fn default() -> Self { + Self { parser: SearchResultParser::new( ".no-results", ".results>.result", ".result__title>.result__a", ".result__url", ".result__snippet", - )?, - }) + ) + .expect("somehow you changed the static stings in the binary i guess"), + } } } diff --git a/src/engines/librex.rs b/src/engines/librex.rs index 5a0fffe..86053a6 100644 --- a/src/engines/librex.rs +++ b/src/engines/librex.rs @@ -20,22 +20,23 @@ pub struct LibreX { parser: SearchResultParser, } -impl LibreX { +impl Default for LibreX { /// Creates a new instance of LibreX with a default configuration. /// /// # Returns /// /// Returns a `Result` containing `LibreX` if successful, otherwise an `EngineError`. - pub fn new() -> Result { - Ok(Self { + fn default() -> Self { + Self { parser: SearchResultParser::new( ".text-result-container>p", ".text-result-container", ".text-result-wrapper>a>h2", ".text-result-wrapper>a", ".text-result-wrapper>span", - )?, - }) + ) + .expect("somehow you changed the static stings in the binary i guess"), + } } } diff --git a/src/engines/mod.rs b/src/engines/mod.rs index a93c9c2..727747d 100644 --- a/src/engines/mod.rs +++ b/src/engines/mod.rs @@ -3,6 +3,12 @@ //! provide a standard functions to be implemented for all the upstream search engine handling //! code. Moreover, it also provides a custom error for the upstream search engine handling code. +use std::sync::Arc; + +use serde::{Deserialize, Serialize}; + +use crate::models::engine_models::EngineHandler; + pub mod bing; pub mod brave; pub mod duckduckgo; @@ -11,3 +17,66 @@ pub mod mojeek; pub mod search_result_parser; pub mod searx; pub mod startpage; + +/// Struct that keeps track of search engines +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +pub struct Engines { + bing: bool, + brave: bool, + duckduckgo: bool, + librex: bool, + mojeek: bool, + search_result_parser: bool, + searx: bool, + startpage: bool, +} + +impl Default for Engines { + fn default() -> Self { + Self { + bing: true, + brave: true, + duckduckgo: true, + librex: true, + mojeek: true, + search_result_parser: true, + searx: true, + startpage: true, + } + } +} + +impl From<&Engines> for Vec { + fn from(value: &Engines) -> Self { + let mut v = vec![]; + if value.duckduckgo { + let engine = crate::engines::duckduckgo::DuckDuckGo::default(); + v.push(EngineHandler::new("duckduckgo", Arc::new(engine))); + } + if value.searx { + let engine = crate::engines::searx::Searx::default(); + v.push(EngineHandler::new("searx", Arc::new(engine))); + } + if value.brave { + let engine = crate::engines::brave::Brave::default(); + v.push(EngineHandler::new("brave", Arc::new(engine))); + } + if value.startpage { + let engine = crate::engines::startpage::Startpage::default(); + v.push(EngineHandler::new("startpage", Arc::new(engine))); + } + if value.librex { + let engine = crate::engines::librex::LibreX::default(); + v.push(EngineHandler::new("librex", Arc::new(engine))); + } + if value.mojeek { + let engine = crate::engines::mojeek::Mojeek::default(); + v.push(EngineHandler::new("mojeek", Arc::new(engine))); + } + if value.bing { + let engine = crate::engines::bing::Bing::default(); + v.push(EngineHandler::new("bing", Arc::new(engine))); + } + v + } +} diff --git a/src/engines/mojeek.rs b/src/engines/mojeek.rs index f9cf59b..4da13ec 100644 --- a/src/engines/mojeek.rs +++ b/src/engines/mojeek.rs @@ -23,18 +23,19 @@ pub struct Mojeek { parser: SearchResultParser, } -impl Mojeek { +impl Default for Mojeek { /// Creates the Mojeek parser. - pub fn new() -> Result { - Ok(Self { + fn default() -> Self { + Self { parser: SearchResultParser::new( ".result-col", ".results-standard li", "a span.url", "h2 a.title", "p.s", - )?, - }) + ) + .expect("somehow you changed the static stings in the binary i guess"), + } } } diff --git a/src/engines/searx.rs b/src/engines/searx.rs index 6fef473..842c17d 100644 --- a/src/engines/searx.rs +++ b/src/engines/searx.rs @@ -19,18 +19,19 @@ pub struct Searx { parser: SearchResultParser, } -impl Searx { +impl Default for Searx { /// creates a Searx parser - pub fn new() -> Result { - Ok(Self { + fn default() -> Self { + Self { parser: SearchResultParser::new( "#urls>.dialog-error>p", ".result", "h3>a", "h3>a", ".content", - )?, - }) + ) + .expect("somehow you changed the static stings in the binary i guess"), + } } } diff --git a/src/engines/startpage.rs b/src/engines/startpage.rs index 9c8b070..405b900 100644 --- a/src/engines/startpage.rs +++ b/src/engines/startpage.rs @@ -23,18 +23,19 @@ pub struct Startpage { parser: SearchResultParser, } -impl Startpage { +impl Default for Startpage { /// Creates the Startpage parser. - pub fn new() -> Result { - Ok(Self { + fn default() -> Self { + Self { parser: SearchResultParser::new( ".no-results", ".w-gl__result__main", ".w-gl__result-second-line-container>.w-gl__result-title>h3", ".w-gl__result-url", ".w-gl__description", - )?, - }) + ) + .expect("somehow you changed the static stings in the binary i guess"), + } } } diff --git a/src/main.rs b/src/main.rs index 0763538..5220edf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ //! and register all the routes for the `crabbysearch` meta search engine website. #![forbid(unsafe_code, clippy::panic)] -#![deny(missing_docs, clippy::missing_docs_in_private_items, clippy::perf)] +#![deny(missing_docs, clippy::perf)] #![warn(clippy::cognitive_complexity, rust_2018_idioms)] pub mod cache; @@ -50,7 +50,7 @@ async fn main() { config.port, ); - let listener = TcpListener::bind((config.binding_ip.as_str(), config.port)) + let listener = TcpListener::bind((config.binding_ip.clone(), config.port)) .expect("could not create TcpListener"); let public_folder_path: &str = file_path(FileType::Theme).unwrap(); diff --git a/src/models/engine_models.rs b/src/models/engine_models.rs index 26cb21d..6718062 100644 --- a/src/models/engine_models.rs +++ b/src/models/engine_models.rs @@ -1,10 +1,12 @@ //! This module provides the error enum to handle different errors associated while requesting data from //! the upstream search engines with the search query provided by the user. +use crate::engines; + use super::aggregation_models::SearchResult; use error_stack::{Report, Result, ResultExt}; use reqwest::Client; -use std::fmt; +use std::{fmt, sync::Arc}; /// A custom error type used for handle engine associated errors. #[derive(Debug)] @@ -153,14 +155,17 @@ pub trait SearchEngine: Sync + Send { pub struct EngineHandler { /// It stores the engine struct wrapped in a box smart pointer as the engine struct implements /// the `SearchEngine` trait. - engine: Box, + engine: Arc, /// It stores the name of the engine to which the struct is associated to. name: &'static str, } impl Clone for EngineHandler { fn clone(&self) -> Self { - Self::new(self.name).unwrap() + Self { + engine: self.engine.clone(), + name: self.name.clone(), + } } } @@ -174,53 +179,13 @@ impl EngineHandler { /// # Returns /// /// It returns an option either containing the value or a none if the engine is unknown - pub fn new(engine_name: &str) -> Result { - let engine: (&'static str, Box) = - match engine_name.to_lowercase().as_str() { - "duckduckgo" => { - let engine = crate::engines::duckduckgo::DuckDuckGo::new()?; - ("duckduckgo", Box::new(engine)) - } - "searx" => { - let engine = crate::engines::searx::Searx::new()?; - ("searx", Box::new(engine)) - } - "brave" => { - let engine = crate::engines::brave::Brave::new()?; - ("brave", Box::new(engine)) - } - "startpage" => { - let engine = crate::engines::startpage::Startpage::new()?; - ("startpage", Box::new(engine)) - } - "librex" => { - let engine = crate::engines::librex::LibreX::new()?; - ("librex", Box::new(engine)) - } - "mojeek" => { - let engine = crate::engines::mojeek::Mojeek::new()?; - ("mojeek", Box::new(engine)) - } - "bing" => { - let engine = crate::engines::bing::Bing::new()?; - ("bing", Box::new(engine)) - } - _ => { - return Err(Report::from(EngineError::NoSuchEngineFound( - engine_name.to_string(), - ))) - } - }; - - Ok(Self { - engine: engine.1, - name: engine.0, - }) + pub fn new(name: &'static str, engine: Arc) -> Self { + Self { name, engine } } /// This function converts the EngineHandler type into a tuple containing the engine name and /// the associated engine struct. - pub fn into_name_engine(self) -> (&'static str, Box) { + pub fn into_name_engine(self) -> (&'static str, Arc) { (self.name, self.engine) } } diff --git a/src/models/server_models.rs b/src/models/server_models.rs index a536683..81de741 100644 --- a/src/models/server_models.rs +++ b/src/models/server_models.rs @@ -1,11 +1,7 @@ //! This module provides the models to parse cookies and search parameters from the search //! engine website. -use std::borrow::Cow; use serde::Deserialize; - -use crate::config::Style; - /// A named struct which deserializes all the user provided search parameters and stores them. #[derive(Deserialize)] pub struct SearchParams { @@ -19,27 +15,3 @@ pub struct SearchParams { /// search url. pub safesearch: Option, } - -/// A named struct which is used to deserialize the cookies fetched from the client side. -#[allow(dead_code)] -#[derive(Deserialize)] -pub struct Cookie<'a> { - /// It stores the theme name used in the website. - pub theme: Cow<'a, str>, - /// It stores the colorscheme name used for the website theme. - pub colorscheme: Cow<'a, str>, - /// It stores the user selected upstream search engines selected from the UI. - pub engines: Cow<'a, Vec>>, -} - -impl<'a> Cookie<'a> { - /// server_models::Cookie contructor function - pub fn build(style: &'a Style, mut engines: Vec>) -> Self { - engines.sort(); - Self { - theme: Cow::Borrowed(&style.theme), - colorscheme: Cow::Borrowed(&style.colorscheme), - engines: Cow::Owned(engines), - } - } -} diff --git a/src/server/routes/search.rs b/src/server/routes/search.rs index 95013ab..411d64e 100644 --- a/src/server/routes/search.rs +++ b/src/server/routes/search.rs @@ -3,6 +3,7 @@ use crate::{ cache::Cache, config::Config, + engines::Engines, models::{ aggregation_models::SearchResults, engine_models::EngineHandler, @@ -47,18 +48,9 @@ pub async fn search( let cookie = req.cookie("appCookie"); // Get search settings using the user's cookie or from the server's config - let search_settings: server_models::Cookie<'_> = cookie + let search_settings: crate::engines::Engines = cookie .and_then(|cookie_value| serde_json::from_str(cookie_value.value()).ok()) - .unwrap_or_else(|| { - server_models::Cookie::build( - &config.style, - config - .upstream_search_engines - .iter() - .map(|e| Cow::Borrowed(e.as_str())) - .collect(), - ) - }); + .unwrap(); // Closure wrapping the results function capturing local references let get_results = |page| results(config.clone(), cache.clone(), query, page, &search_settings); @@ -140,16 +132,11 @@ async fn results( cache: web::Data, query: &str, page: u32, - search_settings: &server_models::Cookie<'_>, + upstream: &Engines, ) -> Result<(SearchResults, String), Box> { // eagerly parse cookie value to evaluate safe search level - let cache_key = format!( - "search?q={}&page={}&engines={}", - query, - page, - search_settings.engines.join(",") - ); + let cache_key = format!("search?q={}&page={}&engines={:?}", query, page, upstream); // fetch the cached results json. let response = cache.cached_results(&cache_key); @@ -162,20 +149,8 @@ async fn results( // default selected upstream search engines from the config file otherwise // parse the non-empty cookie and grab the user selected engines from the // UI and use that. - let mut results: SearchResults = match search_settings.engines.is_empty() { - false => { - aggregate( - query, - page, - config, - &search_settings - .engines - .iter() - .filter_map(|engine| EngineHandler::new(engine).ok()) - .collect::>(), - ) - .await? - } + let mut results: SearchResults = match true { + false => aggregate(query, page, config, &Vec::::from(upstream)).await?, true => { let mut search_results = SearchResults::default(); search_results.set_no_engines_selected();