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
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::{Read, Write};
use crate::destination::kinds::DestinationKind;
use crate::{DestinationConfig, MessageRoutingBehaviour};
use crate::destination::kinds::file::FileDestination;

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub struct Config {
    destinations: Vec<DestinationConfig>,
}

impl Config {
    pub fn get_destinations(&self) -> &Vec<DestinationConfig> {
        &self.destinations
    }
}

impl Default for Config {
    fn default() -> Self {
        #[cfg(target_os = "linux")]
        let log_path = "/var/log/rnotify.log".into();

        #[cfg(target_os = "windows")]
        let log_path = {
            let mut app_data: PathBuf = std::env::var("AppData")
                .expect("Failed to find appdata environment variable, cannot create default configuration file, try creating it manually.")
                .into();
            app_data.push("rnotify");
            app_data.push("rnotify.log");
            app_data
        };
        Self {
            destinations: vec![
                DestinationConfig::new(MessageRoutingBehaviour::Root, DestinationKind::File(FileDestination::new(log_path)), None)
            ]
        }
    }
}

pub fn read_config_file(mut file: File) -> Config {
    let mut s = String::new();
    file.read_to_string(&mut s).expect("Failed to read config file.");
    match toml::from_str(&s) {
        Ok(c) => c,
        Err(err) => panic!("Error parsing config file:{}", err),
    }
}

pub fn fetch_config_file(verbose: bool, config_file_path: &Option<PathBuf>, default_path: &PathBuf) -> File {
    if config_file_path.is_some() {
        return File::options().read(true).open(config_file_path.as_ref().unwrap())
            .expect(&format!("Failed to open config file provided by argument for reading, {:?}", config_file_path));
    }

    let home_dir_path = get_home_dir();
    if verbose {
        println!("HomeDir: '{}'", home_dir_path);
    }

    let mut path_buf: PathBuf = home_dir_path.into();

    if !path_buf.exists() {
        panic!("Home directory does not exist!");
    }
    path_buf.push(default_path);
    if verbose {
        println!("Using config file path: {}", &path_buf.display());
    }

    if !path_buf.exists() {
        println!("Config file doesn't exist, creating it ({})", &path_buf.display());
        let mut file = File::options()
            .create_new(true)
            .write(true)
            .open(&path_buf)
            .expect("Failed to create new config file to write default config.");

        let string = toml::to_string(&Config::default()).expect("Failed to serialize default config.");
        file.write_all(string.as_bytes()).expect("Failed to write default config file to config file.");
        println!("Created default config file");
    }

    File::options()
        .read(true)
        .open(&path_buf).expect("Failed to open config file for reading.")
}

#[cfg(target_os = "windows")]
const HOME_DIR_ENVIRONMENT_VARIABLE: &str = "USERPROFILE";
#[cfg(target_os = "linux")]
const HOME_DIR_ENVIRONMENT_VARIABLE: &str = "HOME";

fn get_home_dir() -> String {
    std::env::var(HOME_DIR_ENVIRONMENT_VARIABLE)
        .expect("Failed to find homedir, in order to find config file, try setting the environment variable.")
}

#[cfg(test)]
mod tests {
    use std::fs;
    use crate::destination::kinds::discord::DiscordDestination;
    use super::*;

    #[test]
    fn test_mixed() {
        let s = fs::read_to_string("test/mixed.toml").expect("Failed to read file");
        let config: Config = toml::from_str(&s).expect("Failed to deserialize.");

        let file_dest = DestinationKind::File(FileDestination::new(PathBuf::from("/var/log/rnotify.log")));
        let dsc_dest = DestinationKind::Discord(DiscordDestination::new("https://discord.com/api/webhooks/11111111111111/2aaaaaaaaaaaaaaaaa".to_owned()));


        let expected_destinations = vec![
            DestinationConfig::new(MessageRoutingBehaviour::Root, file_dest, None),
            DestinationConfig::new(Default::default(), dsc_dest, None),
        ];

        assert_eq!(config.destinations, expected_destinations);
    }
}