File size: 7,338 Bytes
137c62e
 
 
4a505fb
d8bd2fe
137c62e
8b0f2f5
137c62e
a7a28ed
 
 
 
137c62e
 
 
 
 
4402168
c170de8
4402168
c170de8
94ef62e
 
 
 
9cb582a
d8bd2fe
137c62e
 
 
4402168
137c62e
4402168
e2907ed
8e7dc68
d4df901
5aca5c0
6d3396b
d8bd2fe
0527288
 
 
94ef62e
 
 
 
 
0527288
e2907ed
0527288
137c62e
 
 
 
e2907ed
137c62e
4b4dc28
 
 
15dfda6
4b4dc28
137c62e
 
 
e2907ed
137c62e
4b4dc28
89796a0
137c62e
 
 
99936bc
137c62e
 
d8bd2fe
 
 
 
15dfda6
 
 
4b4dc28
d8bd2fe
 
8b0f2f5
 
653d08c
11bcf9c
d8bd2fe
 
 
 
137c62e
 
4402168
137c62e
 
 
 
4402168
e2907ed
 
 
d8bd2fe
 
ff79c1f
 
 
9ee516e
5aca5c0
ff79c1f
6d3396b
d8bd2fe
137c62e
 
 
15dfda6
89796a0
 
 
 
 
 
 
 
 
 
 
 
99936bc
e2907ed
 
 
 
 
 
 
 
 
a7a28ed
 
 
 
e2907ed
 
 
 
 
a7a28ed
 
e2907ed
89796a0
e2907ed
 
 
 
 
 
 
 
 
89796a0
137c62e
15dfda6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//! This module provides the functionality to parse the lua config and convert the config options
//! into rust readable form.

use super::parser_models::Style;
use log::LevelFilter;
use rlua::Lua;
use std::{collections::HashMap, format, fs, path::Path, thread::available_parallelism};

// ------- Constants --------
static COMMON_DIRECTORY_NAME: &str = "websurfx";
static CONFIG_FILE_NAME: &str = "config.lua";

/// 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` - It stores the parsed ip address option on which the server should launch
/// * `style` - It stores the theming options for the website.
/// * `redis_url` - It stores the redis connection url address on which the redis
/// client should connect.
/// * `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.
/// * `request_timeout` - It stores the time (secs) which controls the server request timeout.
/// * `threads` - It stores the number of threads which controls the app will use to run.
#[derive(Clone)]
pub struct Config {
    pub port: u16,
    pub binding_ip: String,
    pub style: Style,
    pub redis_url: String,
    pub aggregator: AggregatorConfig,
    pub logging: bool,
    pub debug: bool,
    pub upstream_search_engines: Vec<crate::engines::engine_models::EngineHandler>,
    pub request_timeout: u8,
    pub threads: u8,
}

/// Configuration options for the aggregator.
///
/// # Fields
///
/// * `random_delay` - It stores the option to whether enable or disable random delays between
/// requests.
#[derive(Clone)]
pub struct AggregatorConfig {
    pub random_delay: bool,
}

impl Config {
    /// A function which parses the config.lua file and puts all the parsed options in the newly
    /// constructed Config struct and returns it.
    ///
    /// # Arguments
    ///
    /// * `logging_initialized` - It takes a boolean which ensures that the logging doesn't get
    /// initialized twice. Pass false if the logger has not yet been initialized.
    ///
    /// # 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 constructed
    /// Config struct with all the parsed config options from the parsed config file.
    pub fn parse(logging_initialized: bool) -> Result<Self, Box<dyn std::error::Error>> {
        Lua::new().context(|context| -> Result<Self, Box<dyn std::error::Error>> {
            let globals = context.globals();

            context
                .load(&fs::read_to_string(Config::config_path()?)?)
                .exec()?;

            let parsed_threads: u8 = globals.get::<_, u8>("threads")?;

            let debug: bool = globals.get::<_, bool>("debug")?;
            let logging:bool= globals.get::<_, bool>("logging")?;
            
            if !logging_initialized {
                set_logging_level(debug, logging);
            }

            let threads: u8 = if parsed_threads == 0 {
                let total_num_of_threads: usize =  available_parallelism()?.get() / 2;
                log::error!("Config Error: The value of `threads` option should be a non zero positive integer");
                log::error!("Falling back to using {} threads", total_num_of_threads);
                total_num_of_threads as u8
            } else {
                parsed_threads
            };

            Ok(Config {
                port: globals.get::<_, u16>("port")?,
                binding_ip: globals.get::<_, String>("binding_ip")?,
                style: Style::new(
                    globals.get::<_, String>("theme")?,
                    globals.get::<_, String>("colorscheme")?,
                ),
                redis_url: globals.get::<_, String>("redis_url")?,
                aggregator: AggregatorConfig {
                    random_delay: globals.get::<_, bool>("production_use")?,
                },
                logging,
                debug,
                upstream_search_engines: globals
                    .get::<_, HashMap<String, bool>>("upstream_search_engines")?
                    .into_iter()
                    .filter_map(|(key, value)| value.then_some(key))
                    .filter_map(|engine| crate::engines::engine_models::EngineHandler::new(&engine))
                    .collect(),
                request_timeout: globals.get::<_, u8>("request_timeout")?,
                threads,
            })
        })
    }

    /// 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.
    fn config_path() -> Result<String, Box<dyn std::error::Error>> {
        // 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!(
                "{}/.config/{}/{}",
                std::env::var("HOME").unwrap(),
                COMMON_DIRECTORY_NAME,
                CONFIG_FILE_NAME
            ));
        }

        // look for config in /etc/xdg
        if Path::new(format!("/etc/xdg/{}/{}", COMMON_DIRECTORY_NAME, CONFIG_FILE_NAME).as_str())
            .exists()
        {
            return Ok("/etc/xdg/websurfx/config.lua".to_string());
        }

        // 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())
    }
}

/// a helper function that sets the proper logging level
fn set_logging_level(debug: bool, logging: bool) {

    if let Ok(pkg_env_var) = std::env::var("PKG_ENV") {
        if pkg_env_var.to_lowercase() == "dev" {
            env_logger::Builder::new()
                .filter(None, LevelFilter::Trace)
                .init();
            return;
        }
    }

    // Initializing logging middleware with level set to default or info.
    let log_level = match (debug, logging) {
        (true, true) => LevelFilter::Error,
        (true, false) => LevelFilter::Debug,
        (false, true) => LevelFilter::Info,
        (false, false) => LevelFilter::Error,
    };

    env_logger::Builder::new().filter(None, log_level).init();
}