NOSHP_Client/
client_config.rs

1use std::{fs, path::Path};
2
3use serde::{Deserialize, Serialize};
4use toml::Table;
5
6use crate::client_types::types::{DeviceCapabilityStatus, DeviceCapabilityType};
7
8#[derive(Serialize, Deserialize)]
9pub struct ClientConfig {
10    pub server_ip: Option<String>,
11    pub device_name: String,
12    pub capability: Table,
13}
14
15#[derive(Clone)]
16pub struct ParsedConfig {
17    pub server_ip: Option<String>,
18    pub device_name: String,
19    pub capabilities: Vec<DeviceCapabilityStatus>,
20}
21
22impl ClientConfig {
23    pub fn load_config(path: &str) -> anyhow::Result<ParsedConfig> {
24        let config_path = Path::new(path);
25
26        if config_path.exists() {
27            let content = fs::read_to_string(config_path)?;
28            let config: ClientConfig = toml::from_str(&content)?;
29
30            let capabilities = config.capability.into_iter().map(|(name, value)|{
31                let value = match value {
32                    toml::Value::Table(v) => v,
33                    _ => {
34                        println!("capability {name} in config file has invalid fields, expected a table as contents");
35                        Table::new()
36                    }
37                };
38
39                let available = match value.get("available") {
40                    Some(v) => v.clone(),
41                    None => toml::Value::Boolean(false)
42                };
43
44                let available = match available {
45                    toml::Value::Boolean(v) => v,
46                    _ => {
47                        eprintln!("Capability {name} in config file has invalid fields, expected a boolean for available field");
48                        false
49                    }
50                };
51
52                let capa_type = match value.get("type") {
53                    Some(v) => v.clone(),
54                    None => {
55                        eprintln!("Capability {name} in config file is missing a type, defaulting to button");
56                        toml::Value::Boolean(false)
57                    }
58                };
59
60                let capa_type = match capa_type {
61                    toml::Value::String(v) => v,
62                    _ => String::from("button")
63                };
64
65                let capa_type = match &*capa_type {
66                    "button" => DeviceCapabilityType::Button,
67                    "slider" => DeviceCapabilityType::Slider,
68                    _ => {
69                        eprintln!("Unrecognized capability type for capability {name}, defaulting to button");
70                        DeviceCapabilityType::Button
71                    }
72                };
73
74                DeviceCapabilityStatus {
75                    capability: name,
76                    available,
77                    r#type: capa_type as i32,
78                    value: None
79                }
80            }).collect();
81
82            return Ok(ParsedConfig {
83                server_ip: config.server_ip,
84                device_name: config.device_name,
85                capabilities,
86            });
87        }
88
89        println!("No config found, generating a default config");
90        let config = ClientConfig::default();
91        let toml = toml::to_string(&config)?;
92
93        fs::write(config_path, toml)?;
94        return Ok(ParsedConfig {
95            server_ip: None,
96            device_name: config.device_name,
97            capabilities: Vec::new(),
98        });
99    }
100}
101
102impl Default for ClientConfig {
103    fn default() -> Self {
104        Self {
105            server_ip: Some(String::from(
106                "Your server IP here, or nothing if you want to set it programmatically",
107            )),
108            device_name: String::from("Unnamed Device"),
109            capability: Table::new(),
110        }
111    }
112}
113
114impl Default for ParsedConfig {
115    fn default() -> Self {
116        Self {
117            server_ip: None,
118            device_name: String::from("Unnamed Device"),
119            capabilities: Vec::new(),
120        }
121    }
122}