Make discovery match the application

Version 0.9.1 Updated Sep 04, 2026

Application names do not need to bend around OrthoConfig's defaults. Put the discovery contract beside the struct when the public flag or filenames are part of the CLI design:

use ortho_config::{OrthoConfig, OrthoResult};
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, OrthoConfig)]
#[ortho_config(
    prefix = "ACME_",
    discovery(
        app_name = "acme-server",
        config_file_name = "server.toml",
        dotfile_name = ".acme-server.toml",
        project_file_name = ".acme-server.toml",
        config_cli_long = "config",
        config_cli_short = 'c',
        config_cli_visible = true
    )
)]
struct Config {
    #[ortho_config(default = 8080)]
    port: u16,
}

fn main() -> OrthoResult<()> {
    let config = Config::load()?;
    println!("port={}", config.port);
    Ok(())
}

This application accepts --config and -c, reads ACME_CONFIG_PATH, looks for .acme-server.toml in project locations, and uses server.toml in platform configuration directories. Keeping these choices in the derive also exposes them through OrthoConfigDocs.

Handle every `load_first` outcome

ConfigDiscovery::load_first distinguishes three outcomes. Ok(Some(...)) contains the first successfully parsed candidate. Ok(None) means discovery had no candidates to try. Err(...) means candidates existed but none loaded successfully; surface or map that error rather than treating it as absence:

use ortho_config::{ConfigDiscovery, OrthoResult};

fn load_discovered_config(discovery: &ConfigDiscovery) -> OrthoResult<()> {
    match discovery.load_first() {
        Ok(Some(_config)) => {
            // Merge or deserialize the discovered Figment value.
            println!("discovery=loaded");
            Ok(())
        }
        Ok(None) => {
            // Continue with application defaults.
            println!("discovery=absent");
            Ok(())
        }
        Err(error) => Err(error),
    }
}

fn main() -> OrthoResult<()> {
    let discovery = ConfigDiscovery::builder("acme").build();
    load_discovered_config(&discovery)
}