irx_config/
lib.rs

1#![doc = include_str!("../README.md")]
2
3pub mod config;
4#[cfg(feature = "parsers")]
5pub mod parsers;
6#[cfg(test)]
7mod tests;
8pub mod value;
9
10use crate::value::SerdeError;
11pub use crate::{
12    config::{Config, ConfigBuilder},
13    value::{json, Value},
14};
15use std::{
16    borrow::Cow, error::Error as StdError, fmt::Debug, io::Error as IoError,
17    result::Result as StdResult,
18};
19
20/// A result type with internal error.
21pub type Result<T> = StdResult<T, Error>;
22
23type AnyError = Box<dyn StdError + Send + Sync + 'static>;
24
25/// A result type with any error.
26pub type AnyResult<T> = StdResult<T, AnyError>;
27
28type CowString<'a> = Cow<'a, str>;
29type AnyParser = Box<dyn Parse>;
30
31/// Default key level separator.
32pub const DEFAULT_KEYS_SEPARATOR: &str = ":";
33
34/// Error generated during any crate operations.
35#[non_exhaustive]
36#[derive(thiserror::Error, Debug)]
37pub enum Error {
38    #[error("Failed to {0} path: '{1}' with empty key path separator")]
39    EmptySeparator(&'static str, String),
40    #[error("Mapping object expected")]
41    NotMap,
42    #[error("{1}")]
43    SerdeError(#[source] SerdeError, Cow<'static, str>),
44    #[error("Failed to parse value for parser #{1}")]
45    ParseValue(#[source] AnyError, usize),
46    #[error("{1}")]
47    IO(#[source] IoError, Cow<'static, str>),
48}
49
50/// Case mode to merging keys during (re)load.
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
52pub enum MergeCase {
53    /// Auto detect merge case mode from appended parsers.
54    #[default]
55    Auto,
56    /// Enforce case sensitive merge mode.
57    Sensitive,
58    /// Enforce case insensitive merge mode.
59    Insensitive,
60}
61
62/// A data structure that has case-sensitive or case-insensitive keys.
63pub trait Case {
64    /// Return `true` if case sensitive, otherwise return `false`.
65    #[inline]
66    fn is_case_sensitive(&self) -> bool {
67        true
68    }
69}
70
71/// A data structure that can be parsed.
72pub trait Parse: Case {
73    /// Parse data to [`Value`] structure. The `value` parameter could hold merged results from previous parser(s)
74    /// call(s). That merged `value` could be used to get some parameter(s) for current parse. For example, path to
75    /// configuration file could be taken from previous command-line parser results (see `FileParser<L>::path_option` or
76    /// `env::ParserBuilder::prefix_option`). If successful then data return as [`Value`] structure.
77    ///
78    /// # Errors
79    ///
80    /// If any errors will occur during parsing then error will be returned.
81    fn parse(&mut self, value: &Value) -> AnyResult<Value>;
82}
83
84impl Case for AnyParser {
85    #[inline]
86    fn is_case_sensitive(&self) -> bool {
87        self.as_ref().is_case_sensitive()
88    }
89}
90
91impl Parse for AnyParser {
92    #[inline]
93    fn parse(&mut self, value: &Value) -> AnyResult<Value> {
94        self.as_mut().parse(value)
95    }
96}
97
98#[inline]
99fn unicase(data: &str) -> String {
100    data.to_lowercase()
101}
102
103#[inline]
104fn normalize_case(data: &str, case_on: bool) -> CowString {
105    if case_on {
106        CowString::Borrowed(data)
107    } else {
108        CowString::Owned(unicase(data))
109    }
110}