2023-04-30 18:16:08 +03:00
//! This module provides the functionality to parse the lua config and convert the config options
//! into rust readable form.
2023-04-30 19:24:16 +03:00
use super ::parser_models ::Style ;
2023-08-02 20:05:39 +03:00
use log ::LevelFilter ;
2023-04-30 18:16:08 +03:00
use rlua ::Lua ;
2023-08-02 20:05:39 +03:00
use std ::{ collections ::HashMap , format , fs , io ::Write , path ::Path , thread ::available_parallelism } ;
2023-04-30 18:16:08 +03:00
2023-05-24 12:01:36 +03:00
// ------- Constants --------
static COMMON_DIRECTORY_NAME : & str = " websurfx " ;
static CONFIG_FILE_NAME : & str = " config.lua " ;
2023-04-30 18:16:08 +03:00
/// 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.
2023-07-03 19:30:25 +02:00
/// * `binding_ip` - It stores the parsed ip address option on which the server should launch
2023-05-02 11:58:21 +03:00
/// * `style` - It stores the theming options for the website.
2023-07-03 19:30:25 +02:00
/// * `redis_url` - It stores the redis connection url address on which the redis
2023-05-02 11:58:21 +03:00
/// client should connect.
2023-07-15 13:36:46 +03:00
/// * `aggregator` - It stores the option to whether enable or disable production use.
/// * `logging` - It stores the option to whether enable or disable logs.
/// * `debug` - It stores the option to whether enable or disable debug mode.
/// * `upstream_search_engines` - It stores all the engine names that were enabled by the user.
2023-07-30 17:08:47 +03:00
/// * `request_timeout` - It stores the time (secs) which controls the server request timeout.
2023-08-02 20:05:39 +03:00
/// * `threads` - It stores the number of threads which controls the app will use to run.
2023-04-30 18:16:08 +03:00
#[ derive(Clone) ]
pub struct Config {
pub port : u16 ,
2023-07-03 19:30:25 +02:00
pub binding_ip : String ,
2023-04-30 18:16:08 +03:00
pub style : Style ,
2023-07-03 19:30:25 +02:00
pub redis_url : String ,
2023-06-29 19:10:09 +02:00
pub aggregator : AggregatorConfig ,
2023-05-27 19:50:20 +03:00
pub logging : bool ,
2023-05-29 21:09:07 +03:00
pub debug : bool ,
2023-07-11 19:38:59 +03:00
pub upstream_search_engines : Vec < String > ,
2023-07-30 10:52:03 +03:00
pub request_timeout : u8 ,
2023-08-02 20:05:39 +03:00
pub threads : u8 ,
2023-05-22 01:13:06 +00:00
}
/// Configuration options for the aggregator.
2023-07-15 13:36:46 +03:00
///
/// # Fields
///
/// * `random_delay` - It stores the option to whether enable or disable random delays between
/// requests.
2023-05-22 01:13:06 +00:00
#[ derive(Clone) ]
2023-06-29 19:10:09 +02:00
pub struct AggregatorConfig {
2023-05-22 01:13:06 +00:00
pub random_delay : bool ,
2023-04-30 18:16:08 +03:00
}
impl Config {
/// A function which parses the config.lua file and puts all the parsed options in the newly
2023-06-29 19:10:09 +02:00
/// constructed Config struct and returns it.
2023-04-30 18:16:08 +03:00
///
/// # Error
///
/// Returns a lua parse error if parsing of the config.lua file fails or has a syntax error
2023-06-29 19:10:09 +02:00
/// or io error if the config.lua file doesn't exists otherwise it returns a newly constructed
2023-04-30 18:16:08 +03:00
/// Config struct with all the parsed config options from the parsed config file.
pub fn parse ( ) -> Result < Self , Box < dyn std ::error ::Error > > {
2023-05-23 23:47:36 +03:00
Lua ::new ( ) . context ( | context | -> Result < Self , Box < dyn std ::error ::Error > > {
2023-04-30 18:16:08 +03:00
let globals = context . globals ( ) ;
context
2023-07-04 15:11:30 -07:00
. load ( & fs ::read_to_string ( Config ::config_path ( ) ? ) ? )
2023-04-30 18:16:08 +03:00
. exec ( ) ? ;
2023-08-02 20:05:39 +03:00
let parsed_threads : u8 = globals . get ::< _ , u8 > ( " threads " ) ? ;
let debug : bool = globals . get ::< _ , bool > ( " debug " ) ? ;
let logging :bool = globals . get ::< _ , bool > ( " logging " ) ? ;
// Initializing logging middleware with level set to default or info.
let mut log_level : LevelFilter = LevelFilter ::Off ;
if logging & & debug = = false {
log_level = LevelFilter ::Info ;
} else if debug {
log_level = LevelFilter ::Trace ;
} ;
env_logger ::Builder ::new ( ) . filter ( None , log_level ) . init ( ) ;
let threads : u8 = if parsed_threads = = 0 {
let total_num_of_threads :usize = available_parallelism ( ) ? . get ( ) / 2 ;
if debug | | logging {
log ::error! ( " Config Error: The value of `threads` option should be a non zero positive integer " ) ;
log ::info! ( " Falling back to using {} threads " , total_num_of_threads )
} else {
std ::io ::stdout ( )
. lock ( )
. write_all ( & format! ( " Config Error: The value of `threads` option should be a non zero positive integer \n Falling back to using {} threads \n " , total_num_of_threads ) . into_bytes ( ) ) ? ;
} ;
2023-08-02 20:07:29 +03:00
total_num_of_threads as u8
2023-08-02 20:05:39 +03:00
} else {
parsed_threads
} ;
2023-04-30 18:16:08 +03:00
Ok ( Config {
port : globals . get ::< _ , u16 > ( " port " ) ? ,
2023-07-03 19:30:25 +02:00
binding_ip : globals . get ::< _ , String > ( " binding_ip " ) ? ,
2023-04-30 18:16:08 +03:00
style : Style ::new (
globals . get ::< _ , String > ( " theme " ) ? ,
globals . get ::< _ , String > ( " colorscheme " ) ? ,
) ,
2023-07-03 19:30:25 +02:00
redis_url : globals . get ::< _ , String > ( " redis_url " ) ? ,
2023-06-29 19:10:09 +02:00
aggregator : AggregatorConfig {
random_delay : globals . get ::< _ , bool > ( " production_use " ) ? ,
} ,
2023-08-02 20:05:39 +03:00
logging ,
debug ,
2023-07-11 19:38:59 +03:00
upstream_search_engines : globals
. get ::< _ , HashMap < String , bool > > ( " upstream_search_engines " ) ?
. into_iter ( )
2023-07-14 21:18:26 +03:00
. filter_map ( | ( key , value ) | value . then_some ( key ) )
2023-07-11 19:38:59 +03:00
. collect ( ) ,
2023-07-30 10:52:03 +03:00
request_timeout : globals . get ::< _ , u8 > ( " request_timeout " ) ? ,
2023-08-02 20:05:39 +03:00
threads ,
2023-04-30 18:16:08 +03:00
} )
} )
}
2023-05-23 23:47:36 +03:00
/// A helper function which returns an appropriate config file path checking if the config
/// file exists on that path.
///
/// # Error
///
/// Returns a `config file not found!!` error if the config file is not present under following
/// paths which are:
/// 1. `~/.config/websurfx/` if it not present here then it fallbacks to the next one (2)
/// 2. `/etc/xdg/websurfx/config.lua` if it is not present here then it fallbacks to the next
/// one (3).
/// 3. `websurfx/` (under project folder ( or codebase in other words)) if it is not present
/// here then it returns an error as mentioned above.
2023-07-04 15:11:30 -07:00
fn config_path ( ) -> Result < String , Box < dyn std ::error ::Error > > {
2023-06-29 19:10:09 +02:00
// check user config
let path = format! (
" {}/.config/{}/config.lua " ,
std ::env ::var ( " HOME " ) . unwrap ( ) ,
COMMON_DIRECTORY_NAME
) ;
if Path ::new ( path . as_str ( ) ) . exists ( ) {
return Ok ( format! (
2023-05-24 12:01:36 +03:00
" {}/.config/{}/{} " ,
std ::env ::var ( " HOME " ) . unwrap ( ) ,
COMMON_DIRECTORY_NAME ,
CONFIG_FILE_NAME
2023-06-29 19:10:09 +02:00
) ) ;
}
// look for config in /etc/xdg
if Path ::new ( format! ( " /etc/xdg/ {} / {} " , COMMON_DIRECTORY_NAME , CONFIG_FILE_NAME ) . as_str ( ) )
2023-05-24 12:01:36 +03:00
. exists ( )
{
2023-06-29 19:10:09 +02:00
return Ok ( " /etc/xdg/websurfx/config.lua " . to_string ( ) ) ;
2023-05-23 23:47:36 +03:00
}
2023-06-29 19:10:09 +02:00
// use dev config
if Path ::new ( format! ( " ./ {} / {} " , COMMON_DIRECTORY_NAME , CONFIG_FILE_NAME ) . as_str ( ) ) . exists ( )
{
return Ok ( " ./websurfx/config.lua " . to_string ( ) ) ;
}
// if no of the configs above exist, return error
Err ( " Config file not found!! " . to_string ( ) . into ( ) )
2023-05-23 23:47:36 +03:00
}
2023-04-30 18:16:08 +03:00
}