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
use std::{fs, path::Path};

use serde::{Deserialize, Serialize};
use toml::Table;

use crate::client_types::types::DeviceCapabilityStatus;

#[derive(Serialize, Deserialize)]
pub struct ClientConfig {
    pub server_ip: Option<String>,
    pub device_name: String,
    pub capability: Table,
}

pub struct ParsedConfig {
    pub server_ip: Option<String>,
    pub device_name: String,
    pub capabilities: Vec<DeviceCapabilityStatus>,
}

impl ClientConfig {
    pub fn load_config(path: &str) -> anyhow::Result<ParsedConfig> {
        let config_path = Path::new(path);

        if config_path.exists() {
            let content = fs::read_to_string(config_path)?;
            let config: ClientConfig = toml::from_str(&content)?;

            let capabilities = config.capability.into_iter().map(|(name, value)|{
                let value = match value {
                    toml::Value::Table(v) => v,
                    _ => {
                        println!("capability {name} in config file has invalid fields, expected a table as contents");
                        Table::new()
                    }
                };

                let available = match value.get("available") {
                    Some(v) => v.clone(),
                    None => toml::Value::Boolean(false)
                };

                let available = match available {
                    toml::Value::Boolean(v) => v,
                    _ => {
                        println!("capability {name} in config file has invalid fields, expected a boolean for available field");
                        false
                    }
                };
                return DeviceCapabilityStatus {
                    capability: name,
                    available
                }
            }).collect();

            return Ok(ParsedConfig {
                server_ip: config.server_ip,
                device_name: config.device_name,
                capabilities,
            });
        }

        println!("No config found, generating a default config");
        let config = ClientConfig::default();
        let toml = toml::to_string(&config)?;

        fs::write(config_path, toml)?;
        return Ok(ParsedConfig {
            server_ip: None,
            device_name: config.device_name,
            capabilities: Vec::new(),
        });
    }
}

impl Default for ClientConfig {
    fn default() -> Self {
        Self {
            server_ip: Some(String::from(
                "Your server IP here, or nothing if you want to set it programmatically",
            )),
            device_name: String::from("Unnamed Device"),
            capability: Table::new(),
        }
    }
}

impl Default for ParsedConfig {
    fn default() -> Self {
        Self {
            server_ip: None,
            device_name: String::from("Unnamed Device"),
            capabilities: Vec::new(),
        }
    }
}