#![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
}
