confget 5.0.1

Parse configuration files.
Documentation
#![warn(missing_docs)]
/*
 * Copyright (c) 2021, 2022  Peter Pentchev <roam@ringlet.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
//! 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.0.1")]
// 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 {}",
        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
}