1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#![warn(missing_docs)]
/*
 * SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
 * SPDX-License-Identifier: BSD-2-Clause
 */
//! Parse configuration files.
//!
//! The `confget` library parses configuration files (currently INI-style
//! files only) and allows a program to use the values defined in them.
//! It provides various options for selecting the variable names and
//! values to return and the configuration file sections to fetch them from.
//!
//! The `confget` library may also be used as a command-line tool with
//! the same interface as the C implementation.
//!
//! # Quick-and-easy parsing of INI-style files
//!
//! The [`read_ini_file`] function will parse
//! an INI-style file and return a hashmap of the values found in its
//! various sections, as well as the name of the first section found in
//! the file (or the one specified in the configuration if
//! the [`Config::section_override`] option is enabled).
//!
//! ```
//! # use std::error::Error;
//! # use std::fs;
//! # use std::path::Path;
//!
//! # use confget::Config;
//!
//! # fn main() -> Result<(), Box<dyn Error>> {
//! # let tempd_obj = tempfile::tempdir()?;
//! # let confdir: &Path = tempd_obj.as_ref();
//! # fs::write(&confdir.join("config.ini"), "var = value\n");
//! // Default configuration except for the filename.
//! let config = Config {
//!     filename: Some(confdir.join("config.ini").to_str().unwrap().to_string()),
//!     ..Config::default()
//! };
//! let (data, first_section) = confget::read_ini_file(&config)?;
//! # Ok(())
//! # }
//! ```
//!
//! For the crate's change history, see
//! the [CHANGES](https://gitlab.com/confget/confget/-/blob/master/CHANGES)
//! file in the source distribution.

#![doc(html_root_url = "https://docs.rs/confget/5.1.2")]
// We want to re-export some defs::* types to make it easier on the consumers.
#![allow(clippy::pub_use)]

#[cfg(feature = "ini-regex")]
use crate::backend::ini_re::IniREBackend;

#[cfg(feature = "ini-nom")]
use crate::backend::ini_nom::IniNomBackend;

use crate::backend::{Backend, DataRead};

pub mod backend;
pub mod defs;
pub mod format;

#[cfg(test)]
pub mod tests;

pub use defs::{BackendKind, ConfgetError, Config, FileData, SectionData};

/// Construct an object of the specified backend type.
///
/// The `backend` member of the [`Config`] object
/// specifies the type of the backend to construct.
///
/// Some backends may perform validation at object creation time;
/// the "ini" one currently does not, so this method will always succeed.
///
/// # Errors
///
/// None for the present, but future backends may check for configuration
/// inconsistencies.
#[inline]
pub fn get_backend<'cfg>(
    config: &'cfg Config,
) -> Result<Box<dyn Backend<'cfg> + 'cfg>, ConfgetError> {
    match config.backend {
        #[cfg(feature = "ini-regex")]
        BackendKind::IniRE => Ok(Box::new(IniREBackend::from_config(config)?)),

        #[cfg(feature = "ini-nom")]
        BackendKind::IniNom => Ok(Box::new(IniNomBackend::from_config(config)?)),
    }
}

/// Read an INI-style file with the specified configuration.
///
/// Internally constructs an "ini" backend and invokes its
/// [`Backend::read_file`] method.
/// The [`Config`] object's `filename` member must contain a value.
///
/// # Errors
///
/// The same as from [`IniREBackend::read_file`].
#[inline]
pub fn read_ini_file(config: &Config) -> Result<DataRead, ConfgetError> {
    #[cfg(feature = "ini-nom")]
    if config.backend == BackendKind::IniNom {
        let ini = IniNomBackend::from_config(config)?;
        return ini.read_file();
    }

    #[cfg(feature = "ini-regex")]
    if config.backend == BackendKind::IniRE {
        let ini = IniREBackend::from_config(config)?;
        return ini.read_file();
    }

    Err(ConfgetError::Config(format!(
        "confget::read_ini_file() invoked for a non-INI-style backend {backend}",
        backend = config.backend.as_ref()
    )))
}

/// Build an array of features supported by the confget library based on
/// the enabled crate features.
#[inline]
#[allow(clippy::must_use_candidate)]
pub fn features() -> Vec<(&'static str, &'static str)> {
    let mut res = vec![
        ("BASE", env!("CARGO_PKG_VERSION")),
        ("REGEX", "1.0"),
        ("REGEX_IMPL_RUST_CRATE_REGEX", "1"),
        ("INI_BACKEND", BackendKind::get_preferred_ini_backend_name()),
    ];

    #[cfg(feature = "ini-nom")]
    {
        res.push(("INI_NOM", "1.0"));
    }

    #[cfg(feature = "ini-regex")]
    {
        res.push(("INI_REGEX", "1.0"));
    }

    res
}