confget/
lib.rs

1#![warn(missing_docs)]
2/*
3 * SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6//! Parse configuration files.
7//!
8//! The `confget` library parses configuration files (currently INI-style
9//! files only) and allows a program to use the values defined in them.
10//! It provides various options for selecting the variable names and
11//! values to return and the configuration file sections to fetch them from.
12//!
13//! The `confget` library may also be used as a command-line tool with
14//! the same interface as the C implementation.
15//!
16//! # Quick-and-easy parsing of INI-style files
17//!
18//! The [`read_ini_file`] function will parse
19//! an INI-style file and return a hashmap of the values found in its
20//! various sections, as well as the name of the first section found in
21//! the file (or the one specified in the configuration if
22//! the [`Config::section_override`] option is enabled).
23//!
24//! ```
25//! # use std::error::Error;
26//! # use std::fs;
27//! # use std::path::Path;
28//!
29//! # use confget::Config;
30//!
31//! # fn main() -> Result<(), Box<dyn Error>> {
32//! # let tempd_obj = tempfile::tempdir()?;
33//! # let confdir: &Path = tempd_obj.as_ref();
34//! # fs::write(&confdir.join("config.ini"), "var = value\n");
35//! // Default configuration except for the filename.
36//! let config = Config {
37//!     filename: Some(confdir.join("config.ini").to_str().unwrap().to_string()),
38//!     ..Config::default()
39//! };
40//! let (data, first_section) = confget::read_ini_file(&config)?;
41//! # Ok(())
42//! # }
43//! ```
44//!
45//! For the crate's change history, see
46//! the [CHANGES](https://gitlab.com/confget/confget/-/blob/master/CHANGES)
47//! file in the source distribution.
48
49#![doc(html_root_url = "https://docs.rs/confget/5.1.2")]
50// We want to re-export some defs::* types to make it easier on the consumers.
51#![allow(clippy::pub_use)]
52
53#[cfg(feature = "ini-regex")]
54use crate::backend::ini_re::IniREBackend;
55
56#[cfg(feature = "ini-nom")]
57use crate::backend::ini_nom::IniNomBackend;
58
59use crate::backend::{Backend, DataRead};
60
61pub mod backend;
62pub mod defs;
63pub mod format;
64
65#[cfg(test)]
66pub mod tests;
67
68pub use defs::{BackendKind, ConfgetError, Config, FileData, SectionData};
69
70/// Construct an object of the specified backend type.
71///
72/// The `backend` member of the [`Config`] object
73/// specifies the type of the backend to construct.
74///
75/// Some backends may perform validation at object creation time;
76/// the "ini" one currently does not, so this method will always succeed.
77///
78/// # Errors
79///
80/// None for the present, but future backends may check for configuration
81/// inconsistencies.
82#[inline]
83pub fn get_backend<'cfg>(
84    config: &'cfg Config,
85) -> Result<Box<dyn Backend<'cfg> + 'cfg>, ConfgetError> {
86    match config.backend {
87        #[cfg(feature = "ini-regex")]
88        BackendKind::IniRE => Ok(Box::new(IniREBackend::from_config(config)?)),
89
90        #[cfg(feature = "ini-nom")]
91        BackendKind::IniNom => Ok(Box::new(IniNomBackend::from_config(config)?)),
92    }
93}
94
95/// Read an INI-style file with the specified configuration.
96///
97/// Internally constructs an "ini" backend and invokes its
98/// [`Backend::read_file`] method.
99/// The [`Config`] object's `filename` member must contain a value.
100///
101/// # Errors
102///
103/// The same as from [`IniREBackend::read_file`].
104#[inline]
105pub fn read_ini_file(config: &Config) -> Result<DataRead, ConfgetError> {
106    #[cfg(feature = "ini-nom")]
107    if config.backend == BackendKind::IniNom {
108        let ini = IniNomBackend::from_config(config)?;
109        return ini.read_file();
110    }
111
112    #[cfg(feature = "ini-regex")]
113    if config.backend == BackendKind::IniRE {
114        let ini = IniREBackend::from_config(config)?;
115        return ini.read_file();
116    }
117
118    Err(ConfgetError::Config(format!(
119        "confget::read_ini_file() invoked for a non-INI-style backend {backend}",
120        backend = config.backend.as_ref()
121    )))
122}
123
124/// Build an array of features supported by the confget library based on
125/// the enabled crate features.
126#[inline]
127#[allow(clippy::must_use_candidate)]
128pub fn features() -> Vec<(&'static str, &'static str)> {
129    let mut res = vec![
130        ("BASE", env!("CARGO_PKG_VERSION")),
131        ("REGEX", "1.0"),
132        ("REGEX_IMPL_RUST_CRATE_REGEX", "1"),
133        ("INI_BACKEND", BackendKind::get_preferred_ini_backend_name()),
134    ];
135
136    #[cfg(feature = "ini-nom")]
137    {
138        res.push(("INI_NOM", "1.0"));
139    }
140
141    #[cfg(feature = "ini-regex")]
142    {
143        res.push(("INI_REGEX", "1.0"));
144    }
145
146    res
147}