cfgmatic-source 5.0.1

Configuration sources (file, env, memory) for cfgmatic framework
Documentation
//! File source example for cfgmatic-source.
//!
//! Demonstrates loading configuration from files with different formats.

use cfgmatic_source::prelude::*;
use serde::Deserialize;
use std::io::Write;

/// Application configuration structure.
#[derive(Debug, Deserialize)]
struct AppConfig {
    name: String,
    version: String,
    #[serde(default)]
    debug: bool,
}

fn main() -> Result<()> {
    println!("=== cfgmatic-source File Example ===\n");

    // Example 1: Load from a single TOML file
    toml_file_example()?;

    // Example 2: Load from a single JSON file
    json_file_example()?;

    // Example 3: Load and merge multiple files
    multiple_files_example()?;

    // Example 4: Optional file handling
    optional_file_example()?;

    println!("\nAll examples completed successfully!");
    Ok(())
}

/// Demonstrates loading from a TOML file.
fn toml_file_example() -> Result<()> {
    println!("--- TOML File Example ---");

    // Create a temporary TOML file
    let mut temp_file = tempfile::NamedTempFile::with_suffix(".toml").unwrap();
    writeln!(
        temp_file,
        r#"
name = "my-app"
version = "1.0.0"
debug = true
"#
    )
    .unwrap();

    let source = FileSource::new(temp_file.path());
    let loader = Loader::new();

    let config: AppConfig = loader.load_as(&source)?;
    println!(
        "Loaded from TOML: name={}, version={}, debug={}",
        config.name, config.version, config.debug
    );

    Ok(())
}

/// Demonstrates loading from a JSON file.
fn json_file_example() -> Result<()> {
    println!("\n--- JSON File Example ---");

    // Create a temporary JSON file
    let mut temp_file = tempfile::NamedTempFile::with_suffix(".json").unwrap();
    writeln!(
        temp_file,
        r#"{{
    "name": "json-app",
    "version": "2.0.0",
    "debug": false
}}"#
    )
    .unwrap();

    let source = FileSource::new(temp_file.path());
    let loader = Loader::new();

    let config: AppConfig = loader.load_as(&source)?;
    println!(
        "Loaded from JSON: name={}, version={}, debug={}",
        config.name, config.version, config.debug
    );

    Ok(())
}

/// Demonstrates loading and merging multiple configuration files.
fn multiple_files_example() -> Result<()> {
    println!("\n--- Multiple Files Example ---");

    // Create base config file
    let mut base_file = tempfile::NamedTempFile::with_suffix(".toml").unwrap();
    writeln!(
        base_file,
        r#"
name = "base-app"
version = "1.0.0"
debug = false
"#
    )
    .unwrap();

    // Create local config file
    let mut local_file = tempfile::NamedTempFile::with_suffix(".toml").unwrap();
    writeln!(
        local_file,
        r"
debug = true
"
    )
    .unwrap();

    // Build source with multiple files (they will be merged)
    let source = FileSource::builder()
        .path(base_file.path())
        .path(local_file.path())
        .build()?;

    let loader = Loader::new();
    let config: AppConfig = loader.load_as(&source)?;

    println!(
        "Merged config: name={}, version={}, debug={}",
        config.name, config.version, config.debug
    );
    println!("  Note: debug=true came from local file");

    Ok(())
}

/// Demonstrates handling optional files.
fn optional_file_example() -> Result<()> {
    println!("\n--- Optional File Example ---");

    // Create a source that points to a non-existent file
    // but mark it as optional
    let source = FileSource::builder()
        .path("/nonexistent/config.toml")
        .required(false)
        .build()?;

    match source.load_raw() {
        Ok(content) if content.is_empty() => {
            println!("Optional file not found, using empty content");
        }
        Ok(content) => {
            println!("Loaded {} bytes", content.len());
        }
        Err(e) => {
            println!("Error loading optional file: {e}");
        }
    }

    // This pattern is useful for user-specific config files
    // that may or may not exist
    println!("  Tip: Use required(false) for user-specific configs");

    Ok(())
}