7 // This software may be used and distributed according to the terms of the |
7 // This software may be used and distributed according to the terms of the |
8 // GNU General Public License version 2 or any later version. |
8 // GNU General Public License version 2 or any later version. |
9 |
9 |
10 use super::layer; |
10 use super::layer; |
11 use crate::config::layer::{ |
11 use crate::config::layer::{ |
12 ConfigError, ConfigLayer, ConfigParseError, ConfigValue, |
12 ConfigError, ConfigLayer, ConfigOrigin, ConfigValue, |
13 }; |
13 }; |
14 use crate::utils::files::get_bytes_from_os_str; |
14 use crate::utils::files::get_bytes_from_os_str; |
15 use format_bytes::{write_bytes, DisplayBytes}; |
15 use format_bytes::{write_bytes, DisplayBytes}; |
16 use std::env; |
16 use std::env; |
17 use std::path::{Path, PathBuf}; |
17 use std::path::{Path, PathBuf}; |
50 pub enum ConfigSource { |
50 pub enum ConfigSource { |
51 /// Absolute path to a config file |
51 /// Absolute path to a config file |
52 AbsPath(PathBuf), |
52 AbsPath(PathBuf), |
53 /// Already parsed (from the CLI, env, Python resources, etc.) |
53 /// Already parsed (from the CLI, env, Python resources, etc.) |
54 Parsed(layer::ConfigLayer), |
54 Parsed(layer::ConfigLayer), |
|
55 } |
|
56 |
|
57 #[derive(Debug)] |
|
58 pub struct ConfigValueParseError { |
|
59 pub origin: ConfigOrigin, |
|
60 pub line: Option<usize>, |
|
61 pub section: Vec<u8>, |
|
62 pub item: Vec<u8>, |
|
63 pub value: Vec<u8>, |
|
64 pub expected_type: &'static str, |
55 } |
65 } |
56 |
66 |
57 pub fn parse_bool(v: &[u8]) -> Option<bool> { |
67 pub fn parse_bool(v: &[u8]) -> Option<bool> { |
58 match v.to_ascii_lowercase().as_slice() { |
68 match v.to_ascii_lowercase().as_slice() { |
59 b"1" | b"yes" | b"true" | b"on" | b"always" => Some(true), |
69 b"1" | b"yes" | b"true" | b"on" | b"always" => Some(true), |
260 |
270 |
261 fn get_parse<'config, T: 'config>( |
271 fn get_parse<'config, T: 'config>( |
262 &'config self, |
272 &'config self, |
263 section: &[u8], |
273 section: &[u8], |
264 item: &[u8], |
274 item: &[u8], |
|
275 expected_type: &'static str, |
265 parse: impl Fn(&'config [u8]) -> Option<T>, |
276 parse: impl Fn(&'config [u8]) -> Option<T>, |
266 ) -> Result<Option<T>, ConfigParseError> { |
277 ) -> Result<Option<T>, ConfigValueParseError> { |
267 match self.get_inner(§ion, &item) { |
278 match self.get_inner(§ion, &item) { |
268 Some((layer, v)) => match parse(&v.bytes) { |
279 Some((layer, v)) => match parse(&v.bytes) { |
269 Some(b) => Ok(Some(b)), |
280 Some(b) => Ok(Some(b)), |
270 None => Err(ConfigParseError { |
281 None => Err(ConfigValueParseError { |
271 origin: layer.origin.to_owned(), |
282 origin: layer.origin.to_owned(), |
272 line: v.line, |
283 line: v.line, |
273 bytes: v.bytes.to_owned(), |
284 value: v.bytes.to_owned(), |
|
285 section: section.to_owned(), |
|
286 item: item.to_owned(), |
|
287 expected_type, |
274 }), |
288 }), |
275 }, |
289 }, |
276 None => Ok(None), |
290 None => Ok(None), |
277 } |
291 } |
278 } |
292 } |
281 /// Otherwise, returns an `Ok(value)` if found, or `None`. |
295 /// Otherwise, returns an `Ok(value)` if found, or `None`. |
282 pub fn get_str( |
296 pub fn get_str( |
283 &self, |
297 &self, |
284 section: &[u8], |
298 section: &[u8], |
285 item: &[u8], |
299 item: &[u8], |
286 ) -> Result<Option<&str>, ConfigParseError> { |
300 ) -> Result<Option<&str>, ConfigValueParseError> { |
287 self.get_parse(section, item, |value| str::from_utf8(value).ok()) |
301 self.get_parse(section, item, "ASCII or UTF-8 string", |value| { |
|
302 str::from_utf8(value).ok() |
|
303 }) |
288 } |
304 } |
289 |
305 |
290 /// Returns an `Err` if the first value found is not a valid unsigned |
306 /// Returns an `Err` if the first value found is not a valid unsigned |
291 /// integer. Otherwise, returns an `Ok(value)` if found, or `None`. |
307 /// integer. Otherwise, returns an `Ok(value)` if found, or `None`. |
292 pub fn get_u32( |
308 pub fn get_u32( |
293 &self, |
309 &self, |
294 section: &[u8], |
310 section: &[u8], |
295 item: &[u8], |
311 item: &[u8], |
296 ) -> Result<Option<u32>, ConfigParseError> { |
312 ) -> Result<Option<u32>, ConfigValueParseError> { |
297 self.get_parse(section, item, |value| { |
313 self.get_parse(section, item, "valid integer", |value| { |
298 str::from_utf8(value).ok()?.parse().ok() |
314 str::from_utf8(value).ok()?.parse().ok() |
299 }) |
315 }) |
300 } |
316 } |
301 |
317 |
302 /// Returns an `Err` if the first value found is not a valid file size |
318 /// Returns an `Err` if the first value found is not a valid file size |
304 /// Otherwise, returns an `Ok(value_in_bytes)` if found, or `None`. |
320 /// Otherwise, returns an `Ok(value_in_bytes)` if found, or `None`. |
305 pub fn get_byte_size( |
321 pub fn get_byte_size( |
306 &self, |
322 &self, |
307 section: &[u8], |
323 section: &[u8], |
308 item: &[u8], |
324 item: &[u8], |
309 ) -> Result<Option<u64>, ConfigParseError> { |
325 ) -> Result<Option<u64>, ConfigValueParseError> { |
310 self.get_parse(section, item, parse_byte_size) |
326 self.get_parse(section, item, "byte quantity", parse_byte_size) |
311 } |
327 } |
312 |
328 |
313 /// Returns an `Err` if the first value found is not a valid boolean. |
329 /// Returns an `Err` if the first value found is not a valid boolean. |
314 /// Otherwise, returns an `Ok(option)`, where `option` is the boolean if |
330 /// Otherwise, returns an `Ok(option)`, where `option` is the boolean if |
315 /// found, or `None`. |
331 /// found, or `None`. |
316 pub fn get_option( |
332 pub fn get_option( |
317 &self, |
333 &self, |
318 section: &[u8], |
334 section: &[u8], |
319 item: &[u8], |
335 item: &[u8], |
320 ) -> Result<Option<bool>, ConfigParseError> { |
336 ) -> Result<Option<bool>, ConfigValueParseError> { |
321 self.get_parse(section, item, parse_bool) |
337 self.get_parse(section, item, "boolean", parse_bool) |
322 } |
338 } |
323 |
339 |
324 /// Returns the corresponding boolean in the config. Returns `Ok(false)` |
340 /// Returns the corresponding boolean in the config. Returns `Ok(false)` |
325 /// if the value is not found, an `Err` if it's not a valid boolean. |
341 /// if the value is not found, an `Err` if it's not a valid boolean. |
326 pub fn get_bool( |
342 pub fn get_bool( |
327 &self, |
343 &self, |
328 section: &[u8], |
344 section: &[u8], |
329 item: &[u8], |
345 item: &[u8], |
330 ) -> Result<bool, ConfigError> { |
346 ) -> Result<bool, ConfigValueParseError> { |
331 Ok(self.get_option(section, item)?.unwrap_or(false)) |
347 Ok(self.get_option(section, item)?.unwrap_or(false)) |
332 } |
348 } |
333 |
349 |
334 /// Returns the raw value bytes of the first one found, or `None`. |
350 /// Returns the raw value bytes of the first one found, or `None`. |
335 pub fn get(&self, section: &[u8], item: &[u8]) -> Option<&[u8]> { |
351 pub fn get(&self, section: &[u8], item: &[u8]) -> Option<&[u8]> { |