neda_lib/client/
config_reader.rs1use gtoml::{get, parse};
2use std::{env, fs, path::Path};
3
4#[derive(Debug)]
5pub struct ApiConfig {
6 pub city: String,
7 pub country: String,
8}
9
10#[derive(Debug)]
11pub struct AdhanConfig {
12 pub enabled: bool,
13 pub file: String,
14}
15
16#[derive(Debug)]
17pub struct DbConfig {
18 pub path: String,
19}
20
21#[derive(Debug)]
22pub struct Config {
23 pub api: ApiConfig,
24 pub adhan: AdhanConfig,
25 pub db: DbConfig,
26}
27
28#[derive(Debug)]
29pub enum ConfigError {
30 Io(std::io::Error),
31 ParseError(String),
32 MissingField(String),
33}
34
35impl Config {
36 pub fn load() -> Result<Self, ConfigError> {
37 let config_dir_path = match env::var("XDG_CONFIG_HOME") {
38 Ok(path) => path + "/neda",
39 Err(_) => env::var("HOME").unwrap() + "/.config/neda",
40 };
41
42 let data_dir_path = match env::var("XDG_DATA_HOME") {
43 Ok(path) => path + "/neda",
44 Err(_) => env::var("HOME").unwrap() + "/.local/share/neda",
45 };
46
47 fs::create_dir_all(&config_dir_path).map_err(ConfigError::Io)?;
48 fs::create_dir_all(&data_dir_path).map_err(ConfigError::Io)?;
49
50 let config_path = config_dir_path + "/config.toml";
51
52 const DEFAULT_CONFIG: &str = include_str!("../../assets/templetes/config.toml");
53
54 if !Path::new(&config_path).exists() {
55 fs::write(&config_path, DEFAULT_CONFIG).map_err(ConfigError::Io)?;
56 }
57
58 let config: String = fs::read_to_string(&config_path).map_err(ConfigError::Io)?;
59 let config = parse(&config).map_err(|e| ConfigError::ParseError(e.to_string()))?;
60
61 Ok(Config {
62 api: ApiConfig {
63 city: match get(&config, "api.city")
64 .map_err(|e| ConfigError::ParseError(e.to_string()))?
65 {
66 Some(value) => value
67 .as_str()
68 .ok_or_else(|| {
69 ConfigError::ParseError("api.city is not a string".to_string())
70 })?
71 .to_string(),
72 None => {
73 return Err(ConfigError::MissingField(
74 "api.city is not set in config.toml".to_string(),
75 ));
76 }
77 },
78 country: match get(&config, "api.country")
79 .map_err(|e| ConfigError::ParseError(e.to_string()))?
80 {
81 Some(value) => value
82 .as_str()
83 .ok_or_else(|| {
84 ConfigError::ParseError("api.country is not a string".to_string())
85 })?
86 .to_string(),
87 None => {
88 return Err(ConfigError::MissingField(
89 "api.country is not set in config.toml".to_string(),
90 ));
91 }
92 },
93 },
94 adhan: AdhanConfig {
95 enabled: match get(&config, "adhan.enabled")
96 .map_err(|e| ConfigError::ParseError(e.to_string()))?
97 {
98 Some(value) => value.as_bool().ok_or_else(|| {
99 ConfigError::ParseError("adhan.enabled is not a boolean".to_string())
100 })?,
101 None => {
102 return Err(ConfigError::MissingField(
103 "adhan.enabled is not set in config.toml".to_string(),
104 ));
105 }
106 },
107 file: match get(&config, "adhan.file")
108 .map_err(|e| ConfigError::ParseError(e.to_string()))?
109 {
110 Some(value) => value
111 .as_str()
112 .ok_or_else(|| {
113 ConfigError::ParseError("adhan.file is not a string".to_string())
114 })?
115 .to_string(),
116 None => {
117 return Err(ConfigError::MissingField(
118 "adhan.file is not set in config.toml".to_string(),
119 ));
120 }
121 },
122 },
123 db: DbConfig {
124 path: match get(&config, "db.path")
125 .map_err(|e| ConfigError::ParseError(e.to_string()))?
126 {
127 Some(value) => value
128 .as_str()
129 .ok_or_else(|| {
130 ConfigError::ParseError("db.path is not a string".to_string())
131 })?
132 .to_string(),
133 None => {
134 return Err(ConfigError::MissingField(
135 "db.path is not set in config.toml".to_string(),
136 ));
137 }
138 },
139 },
140 })
141 }
142}