volga 0.9.0

Easy & Fast Web Framework for Rust
Documentation
//! File-based application configuration.
//!
//! # Quick start
//! ```no_run
//! use volga::{App, Config};
//! use serde::Deserialize;
//!
//! #[derive(Deserialize)]
//! struct Database { url: String }
//!
//! #[tokio::main]
//! async fn main() -> std::io::Result<()> {
//!     let app = App::new()
//!         .with_config(|cfg| cfg.with_file("app_config.toml").bind_section::<Database>("database"));
//!     app.run().await
//! }
//! ```

pub(crate) mod builder;
pub(crate) mod extractor;
pub(crate) mod processing;
pub(crate) mod store;

pub use builder::ConfigBuilder;
pub use extractor::Config;
pub use store::{ConfigStore, SectionKind};

use crate::App;

impl App {
    /// Loads configuration from the default file (`app_config.toml` or `app_config.json`).
    ///
    /// Searches the current working directory in order: `app_config.toml`, then `app_config.json`.
    ///
    /// **Strict:** panics at startup if neither file exists or if config processing fails.
    /// If you want optional file-based config, use [`App::with_config`] directly.
    ///
    /// # Panics
    ///
    /// Panics if no default config file is found or if the config fails to load or parse.
    pub fn with_default_config(self) -> Self {
        let path = builder::get_default_file().unwrap_or_else(|| {
            panic!(
                "config: with_default_config() found neither app_config.toml nor app_config.json"
            )
        });
        self.process_config(ConfigBuilder::from_file(path))
            .unwrap_or_else(|e| panic!("config: {e}"))
    }

    /// Configures file-based configuration via a builder closure.
    ///
    /// # Example
    /// ```no_run
    /// use volga::App;
    /// use serde::Deserialize;
    /// #[derive(Deserialize)] struct Database { url: String }
    ///
    /// #[tokio::main]
    /// async fn main() -> std::io::Result<()> {
    ///     let app = App::new().with_config(|cfg| {
    ///         cfg.with_file("config/prod.toml")
    ///            .bind_section::<Database>("database")
    ///            .reload_on_change()
    ///     });
    ///     app.run().await
    /// }
    /// ```
    ///
    /// # Panics
    ///
    /// Panics if the config file cannot be read, parsed, or if any required section is missing.
    pub fn with_config<F>(self, f: F) -> Self
    where
        F: FnOnce(ConfigBuilder) -> ConfigBuilder,
    {
        self.process_config(f(ConfigBuilder::new()))
            .unwrap_or_else(|e| panic!("config: {e}"))
    }

    /// Sets the file-based configuration
    ///
    /// # Example
    /// ```no_run
    /// use volga::{App, ConfigBuilder};
    /// use serde::Deserialize;
    /// #[derive(Deserialize)] struct Database { url: String }
    ///
    /// #[tokio::main]
    /// async fn main() -> std::io::Result<()> {
    ///     let config = ConfigBuilder::from_file("config/prod.toml")
    ///         .bind_section::<Database>("database")
    ///         .reload_on_change();
    ///
    ///     let app = App::new().set_config(config);
    ///     app.run().await
    /// }
    /// ```
    pub fn set_config(self, config: ConfigBuilder) -> Self {
        self.process_config(config)
            .unwrap_or_else(|e| panic!("config: {e}"))
    }
}

#[cfg(test)]
mod tests {
    use crate::App;

    #[test]
    #[should_panic(expected = "config:")]
    fn with_default_config_panics_when_no_default_file() {
        // This test relies on neither app_config.toml nor app_config.json
        // existing in the current working directory during test runs.
        App::new().with_default_config();
    }
}