replace commandline arguments with config.lua & add support for changing themes & coloschemes

This commit is contained in:
neon_arch 2023-04-30 18:16:08 +03:00
parent fe8f5dee43
commit 137c62ed5f
17 changed files with 209 additions and 228 deletions

View file

@ -3,50 +3,10 @@
//! This module contains the main function which handles the logging of the application to the
//! stdout and handles the command line arguments provided and launches the `websurfx` server.
use std::{ops::RangeInclusive, net::TcpListener};
use std::net::TcpListener;
use clap::{command, Parser};
use env_logger::Env;
use websurfx::run;
/// A commandline arguments struct.
#[derive(Parser, Debug, Default)]
#[clap(author = "neon_arch", version, about = "Websurfx server application")]
#[command(propagate_version = true)]
struct CliArgs {
#[clap(default_value_t = 8080, short, long,value_parser = is_port_in_range)]
/// provide port number in range [1024-65536] to launch the server on.
port: u16,
}
const PORT_RANGE: RangeInclusive<usize> = 1024..=65535;
/// A function to check whether port is valid u32 number or is in range
/// between [1024-65536] otherwise display an appropriate error message.
///
/// # Arguments
///
/// * `s` - Takes a commandline argument port as a string.
///
/// # Error
///
/// Check whether the provided argument to `--port` commandline option is a valid
/// u16 argument and returns it as a u16 value otherwise returns an error with an
/// appropriate error message.
fn is_port_in_range(s: &str) -> Result<u16, String> {
let port: usize = s
.parse()
.map_err(|_| format!("`{s}` is not a valid port number"))?;
if PORT_RANGE.contains(&port) {
Ok(port as u16)
} else {
Err(format!(
"port not found in range {}-{}",
PORT_RANGE.start(),
PORT_RANGE.end()
))
}
}
use websurfx::{config_parser::parser::Config, run};
/// The function that launches the main server and registers all the routes of the website.
///
@ -56,14 +16,15 @@ fn is_port_in_range(s: &str) -> Result<u16, String> {
/// available for being used for other applications.
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let args = CliArgs::parse();
// Initialize the parsed config file.
let config = Config::parse().unwrap();
// Initializing logging middleware with level set to default or info.
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
log::info!("started server on port {}", args.port);
log::info!("started server on port {}", config.port);
let listener = TcpListener::bind(("127.0.0.1", args.port))?;
let listener = TcpListener::bind((config.binding_ip_addr.clone(), config.port))?;
run(listener)?.await
run(listener, config)?.await
}

1
src/config_parser/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod parser;

View file

@ -0,0 +1,62 @@
//! This module provides the functionality to parse the lua config and convert the config options
//! into rust readable form.
use rlua::Lua;
use serde::Serialize;
use std::fs;
#[derive(Serialize, Clone)]
pub struct Style {
pub theme: String,
pub colorscheme: String,
}
impl Style {
pub fn new(theme: String, colorscheme: String) -> Self {
Style { theme, colorscheme }
}
}
/// A named struct which stores the parsed config file options.
///
/// # Fields
//
/// * `port` - It stores the parsed port number option on which the server should launch.
/// * `binding_ip_addr` - It stores the parsed ip address option on which the server should launch
#[derive(Clone)]
pub struct Config {
pub port: u16,
pub binding_ip_addr: String,
pub style: Style,
}
impl Config {
/// A function which parses the config.lua file and puts all the parsed options in the newly
/// contructed Config struct and returns it.
///
/// # Error
///
/// Returns a lua parse error if parsing of the config.lua file fails or has a syntax error
/// or io error if the config.lua file doesn't exists otherwise it returns a newly contructed
/// Config struct with all the parsed config options from the parsed config file.
pub fn parse() -> Result<Self, Box<dyn std::error::Error>> {
let lua = Lua::new();
lua.context(|context| {
let globals = context.globals();
context
.load(&fs::read_to_string("./websurfx/config.lua")?)
.exec()?;
Ok(Config {
port: globals.get::<_, u16>("port")?,
binding_ip_addr: globals.get::<_, String>("binding_ip_addr")?,
style: Style::new(
globals.get::<_, String>("theme")?,
globals.get::<_, String>("colorscheme")?,
),
})
})
}
}

View file

@ -1,21 +1,22 @@
//! This main library module provides the functionality to provide and handle the Tcp server
//! and register all the routes for the `websurfx` meta search engine website.
pub mod config_parser;
pub mod engines;
pub mod server;
pub mod search_results_handler;
pub mod server;
use std::net::TcpListener;
use crate::server::routes;
use actix_files as fs;
use actix_web::{middleware::Logger, web, App, HttpServer, dev::Server};
use actix_web::{dev::Server, middleware::Logger, web, App, HttpServer};
use config_parser::parser::Config;
use handlebars::Handlebars;
/// Runs the web server on the provided TCP listener and returns a `Server` instance.
///
///
/// # Arguments
///
/// * `listener` - A `TcpListener` instance representing the address and port to listen on.
@ -25,7 +26,7 @@ use handlebars::Handlebars;
/// Returns a `Result` containing a `Server` instance on success, or an `std::io::Error` on failure.
///
/// # Example
///
///
/// ```rust
/// use std::net::TcpListener;
/// use websurfx::run;
@ -33,7 +34,7 @@ use handlebars::Handlebars;
/// let listener = TcpListener::bind("127.0.0.1:8080").expect("Failed to bind address");
/// let server = run(listener).expect("Failed to start server");
/// ```
pub fn run(listener: TcpListener) -> std::io::Result<Server> {
pub fn run(listener: TcpListener, config: Config) -> std::io::Result<Server> {
let mut handlebars: Handlebars = Handlebars::new();
handlebars
@ -45,6 +46,7 @@ pub fn run(listener: TcpListener) -> std::io::Result<Server> {
let server = HttpServer::new(move || {
App::new()
.app_data(handlebars_ref.clone())
.app_data(web::Data::new(config.clone()))
.wrap(Logger::default()) // added logging middleware for logging.
// Serve images and static files (css and js files).
.service(fs::Files::new("/static", "./public/static").show_files_listing())

View file

@ -3,6 +3,8 @@
use serde::Serialize;
use crate::config_parser::parser::Style;
/// A named struct to store and serialize the individual search result from all the scraped
/// and aggregated search results from the upstream search engines.
///
@ -117,11 +119,12 @@ impl RawSearchResult {
/// * `results` - Stores the individual serializable `SearchResult` struct into a vector of
/// `SearchResult` structs.
/// * `page_query` - Stores the current pages search query `q` provided in the search url.
#[derive(Debug, Serialize)]
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchResults {
pub results: Vec<SearchResult>,
pub page_query: String,
pub style: Style,
}
impl SearchResults {
@ -137,6 +140,11 @@ impl SearchResults {
SearchResults {
results,
page_query,
style: Style::new("".to_string(), "".to_string()),
}
}
pub fn add_style(&mut self, style: Style) {
self.style = style;
}
}

View file

@ -4,7 +4,7 @@
use std::fs::read_to_string;
use crate::search_results_handler::aggregator::aggregate;
use crate::{config_parser::parser::Config, search_results_handler::aggregator::aggregate};
use actix_web::{get, web, HttpRequest, HttpResponse};
use handlebars::Handlebars;
use serde::Deserialize;
@ -27,8 +27,9 @@ struct SearchParams {
#[get("/")]
pub async fn index(
hbs: web::Data<Handlebars<'_>>,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = hbs.render("index", &"").unwrap();
let page_content: String = hbs.render("index", &config.style).unwrap();
Ok(HttpResponse::Ok().body(page_content))
}
@ -36,8 +37,9 @@ pub async fn index(
/// website essentially the 404 error page.
pub async fn not_found(
hbs: web::Data<Handlebars<'_>>,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = hbs.render("404", &"")?;
let page_content: String = hbs.render("404", &config.style)?;
Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
@ -52,7 +54,7 @@ pub async fn not_found(
/// ```bash
/// curl "http://127.0.0.1:8080/search?q=sweden&page=1"
/// ```
///
///
/// Or
///
/// ```bash
@ -62,6 +64,7 @@ pub async fn not_found(
pub async fn search(
hbs: web::Data<Handlebars<'_>>,
req: HttpRequest,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let params = web::Query::<SearchParams>::from_query(req.query_string())?;
match &params.q {
@ -71,8 +74,9 @@ pub async fn search(
.insert_header(("location", "/"))
.finish())
} else {
let results_json: crate::search_results_handler::aggregation_models::SearchResults =
let mut results_json: crate::search_results_handler::aggregation_models::SearchResults =
aggregate(query, params.page).await?;
results_json.add_style(config.style.clone());
let page_content: String = hbs.render("search", &results_json)?;
Ok(HttpResponse::Ok().body(page_content))
}
@ -96,8 +100,9 @@ pub async fn robots_data(_req: HttpRequest) -> Result<HttpResponse, Box<dyn std:
#[get("/about")]
pub async fn about(
hbs: web::Data<Handlebars<'_>>,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = hbs.render("about", &"")?;
let page_content: String = hbs.render("about", &config.style)?;
Ok(HttpResponse::Ok().body(page_content))
}
@ -105,8 +110,9 @@ pub async fn about(
#[get("/settings")]
pub async fn settings(
hbs: web::Data<Handlebars<'_>>,
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
let page_content: String = hbs.render("settings", &"")?;
let page_content: String = hbs.render("settings", &config.style)?;
Ok(HttpResponse::Ok().body(page_content))
}