1use super::toml;
2use super::storage;
3
4use std::io;
5use std::env;
6use std::fs;
7
8use std::fs::File;
9use std::io::prelude::*;
10use std::path::PathBuf;
11use std::collections::HashSet;
12use std::collections::HashMap;
13use toml::ValueImpl;
14
15pub struct StorageConfig {
16 default: String,
17 local_search_paths: Vec<String>,
18 storages: HashMap<String, PathBuf>
19}
20
21pub struct Config {
22 value: Option<toml::Value>,
23 storage: StorageConfig
24}
25
26#[derive(Deserialize, Serialize)]
27struct ParseStorage {
28 name: String,
29 path: PathBuf
30}
31
32#[derive(Deserialize, Serialize)]
33struct ParseStorageConfig {
34 default: Option<String>,
35 local_search_paths: Option<Vec<String>>,
36 storages: Option<Vec<ParseStorage>>,
37}
38
39#[derive(Debug)]
40pub enum ConfigError {
41 Read(io::Error),
42 Parse(toml::de::Error),
43 InvalidStorage,
44 NoStorage,
45 NoStorages,
46 RedundantStorages,
47 InvalidDefaultStorage
48}
49
50impl Config {
51 pub fn load_default() -> Result<Config, ConfigError> {
56 use toml::LoadError;
58 let value = match toml::Value::load(Config::config_path()) {
59 Ok(a) => a,
60 Err(LoadError::Open(_)) => return Ok(Config::default_config()),
61 Err(LoadError::Read(e)) => return Err(ConfigError::Read(e)),
62 Err(LoadError::Parse(e)) => return Err(ConfigError::Parse(e)),
63 };
64
65 let storage = match value.get("storage") {
66 None => return Err(ConfigError::NoStorage),
67 Some(a) => match &a.clone().try_into::<ParseStorageConfig>() {
68 &Ok(ref a) => Config::parse_storage_config(a)?,
69 _ => return Err(ConfigError::InvalidStorage),
70 },
71 };
72
73 Ok(Config{value: Some(value), storage})
74 }
75
76 pub fn load_storage(&self, name: &str)
81 -> Result<storage::Storage, storage::LoadStorageError> {
82 let path = match self.storage.storages.get(name) {
83 Some(a) => a.clone(),
84 None => return Err(storage::LoadStorageError::InvalidName),
85 };
86
87 storage::Storage::load(self, name, path)
88 }
89
90 pub fn load_default_storage(&self)
92 -> Result<storage::Storage, storage::LoadStorageError> {
93 self.load_storage(&self.storage.default)
94 }
95
96 pub fn load_local_storage(&self)
103 -> Result<storage::Storage, storage::LoadStorageError> {
104 let mut cwd = env::current_dir().expect("Failed to get current dir");
105 'outer: loop {
106 let mut npath = PathBuf::from(cwd.clone());
107 for spath in &self.storage.local_search_paths {
108 npath.push(spath);
109 if !npath.is_dir() {
110 println!("{:?} isn't a dir", npath);
111 continue;
112 }
113
114 npath.push("storage");
115 if !npath.is_file() {
116 println!("{:?} isn't a file", npath);
117 continue;
118 }
119
120 npath.pop(); let name = cwd.file_name()
122 .map(|v| v.to_str().expect("Invalid path"))
123 .unwrap_or("root").to_string();
124 return storage::Storage::load(self, &name, npath)
125 }
126
127 if !cwd.pop() {
128 return Err(storage::LoadStorageError::NotFound);
129 }
130 }
131 }
132
133 pub fn config_folder() -> PathBuf {
134 let mut p = Config::home_dir();
135 p.push(".config");
136 p.push("nodes");
137 p
138 }
139
140 pub fn config_path() -> PathBuf {
141 let mut p = Config::config_folder();
142 p.push("config");
143 p
144 }
145
146
147 pub fn value(&self) -> &Option<toml::Value> {
149 &self.value
150 }
151
152 fn default_config() -> Config {
154 let mut storages = HashMap::new();
155
156 let mut storage = Config::default_storage_path();
160 if !storage.is_dir() {
161 fs::create_dir_all(&storage)
162 .expect("Failed to create default storage path");
163
164 storage.push("storage");
165 File::create(&storage)
166 .and_then(|mut f| f.write_all(b"last_id = 0"))
167 .expect("Unable to create default storage file");
168 storage.pop();
169
170 storage.push("nodes");
171 fs::create_dir_all(&storage).unwrap();
172 storage.pop();
173
174 storage.push("meta");
175 fs::create_dir_all(&storage).unwrap();
176 storage.pop();
177 }
178
179 storages.insert("default".to_string(), storage);
180 Config {
181 value: None,
182 storage: StorageConfig {
183 default: "default".to_string(),
184 local_search_paths: Config::default_local_search_paths(),
185 storages,
186 }
187 }
188 }
189
190 fn parse_storage_config(config: &ParseStorageConfig)
191 -> Result<StorageConfig, ConfigError> {
192 let mut paths = HashSet::new();
193 let mut storages = HashMap::new();
194
195 let cstorages = match &config.storages {
197 &Some(ref a) => a,
198 &None => return Err(ConfigError::NoStorages),
199 };
200
201 if cstorages.is_empty() {
202 return Err(ConfigError::NoStorages);
203 }
204
205 for storage in cstorages {
206 if !paths.insert(storage.path.clone()) {
207 return Err(ConfigError::RedundantStorages);
208 }
209
210 let v = storages.insert(
211 storage.name.clone(),
212 storage.path.clone()
213 );
214
215 if v.is_some() {
216 return Err(ConfigError::RedundantStorages);
217 }
218 }
219
220 let default = config.default.clone()
223 .unwrap_or(cstorages.first().unwrap().name.clone());
224 if storages.get(&default).is_none() {
225 return Err(ConfigError::InvalidDefaultStorage);
226 }
227
228 let local_search_paths = config.local_search_paths.as_ref()
230 .unwrap_or(&Config::default_local_search_paths()).clone();
231 Ok(StorageConfig{storages, local_search_paths, default})
232 }
233
234 fn home_dir() -> PathBuf {
235 env::home_dir().expect("Could not retrieve home directory")
236 }
237
238 fn default_storage_path() -> PathBuf {
239 let mut p = Config::home_dir();
240 p.push(".local");
241 p.push("share");
242 p.push("nodes-test"); p
244 }
245
246 fn default_local_search_paths() -> Vec<String> {
247 vec!(String::from(".nodes"))
248 }
249}