---
title: "Gerenciador de Configuracoes em Rust"
url: "https://rustlang.com.br/projetos/config-manager/"
markdown_url: "https://rustlang.com.br/projetos/config-manager.MD"
description: "Construa um gerenciador de configuracoes em Rust com suporte a multiplos formatos TOML, JSON e YAML, sobrescrita por variaveis de ambiente e validacao."
date: "2026-02-24"
author: "Equipe Rust Brasil"
---

# Gerenciador de Configuracoes em Rust

Construa um gerenciador de configuracoes em Rust com suporte a multiplos formatos TOML, JSON e YAML, sobrescrita por variaveis de ambiente e validacao.


Toda aplicacao de software precisa de configuracao: enderecos de servidores, credenciais, parametros de execucao, limites e opcoes. Gerenciar configuracoes de forma robusta — suportando multiplos formatos de arquivo, variaveis de ambiente e valores padrao com validacao — e um problema que aparece em praticamente todo projeto. Neste walkthrough, vamos construir um **gerenciador de configuracoes completo** que carrega dados de arquivos TOML, JSON ou YAML, permite sobrescrever valores via variaveis de ambiente e valida que a configuracao final e consistente.

Este projeto e excelente para dominar o ecossistema `serde` em Rust, entender traits como `Deserialize` e `Default`, e aprender a projetar APIs ergonomicas que outros desenvolvedores podem usar como biblioteca.

## O Que Vamos Construir

Nosso gerenciador de configuracoes tera os seguintes recursos:

- Carregamento de configuracao a partir de arquivos TOML, JSON ou YAML
- Deteccao automatica do formato pelo nome do arquivo
- Sobrescrita de valores via variaveis de ambiente
- Valores padrao para todos os campos
- Validacao com mensagens de erro descritivas
- Merge de multiplas fontes (arquivo + ambiente + padrao)
- Exibicao da configuracao final formatada
- CLI para inspecionar e validar arquivos de configuracao

## Estrutura do Projeto

```
config-manager/
├── Cargo.toml
└── src/
    ├── main.rs
    ├── config.rs
    ├── carregador.rs
    └── validador.rs
```

## Configurando o Projeto

```bash
cargo new config-manager
cd config-manager
```

Configure o `Cargo.toml`:

```toml
[package]
name = "config-manager"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "0.8"
serde_yaml = "0.9"
clap = { version = "4", features = ["derive"] }
colored = "2"
```

Usamos `serde` como framework central de serializacao, com backends para cada formato: `toml` para TOML, `serde_json` para JSON e `serde_yaml` para YAML. O `clap` fornece a interface de linha de comando e `colored` a saida formatada.

## Passo 1: Definindo a Estrutura de Configuracao

O modulo `config.rs` define a estrutura da configuracao com valores padrao e suporte a serializacao em todos os formatos.

```rust
// src/config.rs
use serde::{Deserialize, Serialize};

/// Configuracao principal da aplicacao
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Configuracao {
    /// Nome da aplicacao
    #[serde(default = "padrao_nome_app")]
    pub nome_app: String,

    /// Ambiente de execucao (desenvolvimento, producao, teste)
    #[serde(default = "padrao_ambiente")]
    pub ambiente: String,

    /// Configuracoes do servidor
    #[serde(default)]
    pub servidor: ConfigServidor,

    /// Configuracoes do banco de dados
    #[serde(default)]
    pub banco_de_dados: ConfigBancoDeDados,

    /// Configuracoes de log
    #[serde(default)]
    pub log: ConfigLog,

    /// Configuracoes de seguranca
    #[serde(default)]
    pub seguranca: ConfigSeguranca,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigServidor {
    /// Endereco de escuta
    #[serde(default = "padrao_host")]
    pub host: String,

    /// Porta do servidor
    #[serde(default = "padrao_porta")]
    pub porta: u16,

    /// Numero maximo de conexoes simultaneas
    #[serde(default = "padrao_max_conexoes")]
    pub max_conexoes: u32,

    /// Timeout de requisicao em segundos
    #[serde(default = "padrao_timeout")]
    pub timeout_segundos: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigBancoDeDados {
    /// URL de conexao com o banco
    #[serde(default = "padrao_url_banco")]
    pub url: String,

    /// Tamanho maximo do pool de conexoes
    #[serde(default = "padrao_pool_max")]
    pub pool_maximo: u32,

    /// Tamanho minimo do pool de conexoes
    #[serde(default = "padrao_pool_min")]
    pub pool_minimo: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigLog {
    /// Nivel de log: trace, debug, info, warn, error
    #[serde(default = "padrao_nivel_log")]
    pub nivel: String,

    /// Formato: texto ou json
    #[serde(default = "padrao_formato_log")]
    pub formato: String,

    /// Caminho do arquivo de log (vazio para stdout)
    #[serde(default)]
    pub arquivo: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigSeguranca {
    /// Chave secreta para tokens
    #[serde(default = "padrao_chave_secreta")]
    pub chave_secreta: String,

    /// Tempo de expiracao do token em horas
    #[serde(default = "padrao_expiracao_token")]
    pub expiracao_token_horas: u32,

    /// Origens permitidas para CORS
    #[serde(default = "padrao_cors_origens")]
    pub cors_origens: Vec<String>,
}

// Funcoes de valores padrao
fn padrao_nome_app() -> String { "minha-app".to_string() }
fn padrao_ambiente() -> String { "desenvolvimento".to_string() }
fn padrao_host() -> String { "127.0.0.1".to_string() }
fn padrao_porta() -> u16 { 8080 }
fn padrao_max_conexoes() -> u32 { 100 }
fn padrao_timeout() -> u64 { 30 }
fn padrao_url_banco() -> String { "sqlite://dados.db".to_string() }
fn padrao_pool_max() -> u32 { 10 }
fn padrao_pool_min() -> u32 { 2 }
fn padrao_nivel_log() -> String { "info".to_string() }
fn padrao_formato_log() -> String { "texto".to_string() }
fn padrao_chave_secreta() -> String { "ALTERAR_EM_PRODUCAO".to_string() }
fn padrao_expiracao_token() -> u32 { 24 }
fn padrao_cors_origens() -> Vec<String> { vec!["http://localhost:3000".to_string()] }

impl Default for Configuracao {
    fn default() -> Self {
        Self {
            nome_app: padrao_nome_app(),
            ambiente: padrao_ambiente(),
            servidor: ConfigServidor::default(),
            banco_de_dados: ConfigBancoDeDados::default(),
            log: ConfigLog::default(),
            seguranca: ConfigSeguranca::default(),
        }
    }
}

impl Default for ConfigServidor {
    fn default() -> Self {
        Self {
            host: padrao_host(),
            porta: padrao_porta(),
            max_conexoes: padrao_max_conexoes(),
            timeout_segundos: padrao_timeout(),
        }
    }
}

impl Default for ConfigBancoDeDados {
    fn default() -> Self {
        Self {
            url: padrao_url_banco(),
            pool_maximo: padrao_pool_max(),
            pool_minimo: padrao_pool_min(),
        }
    }
}

impl Default for ConfigLog {
    fn default() -> Self {
        Self {
            nivel: padrao_nivel_log(),
            formato: padrao_formato_log(),
            arquivo: String::new(),
        }
    }
}

impl Default for ConfigSeguranca {
    fn default() -> Self {
        Self {
            chave_secreta: padrao_chave_secreta(),
            expiracao_token_horas: padrao_expiracao_token(),
            cors_origens: padrao_cors_origens(),
        }
    }
}
```

Cada campo usa `#[serde(default = "funcao")]` para definir um valor padrao quando o campo esta ausente no arquivo. Isso garante que a configuracao sempre tenha valores validos, mesmo que o arquivo contenha apenas uma parte dos campos.

## Passo 2: Carregador Multi-Formato com Override de Ambiente

O modulo `carregador.rs` detecta o formato do arquivo, carrega a configuracao e aplica variaveis de ambiente como sobrescrita.

```rust
// src/carregador.rs
use crate::config::Configuracao;
use std::env;
use std::fs;
use std::path::Path;

/// Formato do arquivo de configuracao
#[derive(Debug, Clone, PartialEq)]
pub enum Formato {
    Toml,
    Json,
    Yaml,
}

/// Erros possiveis durante o carregamento
#[derive(Debug)]
pub enum ErroCarregamento {
    ArquivoNaoEncontrado(String),
    LeituraFalhou(String),
    FormatoDesconhecido(String),
    ParseFalhou { formato: String, detalhe: String },
}

impl std::fmt::Display for ErroCarregamento {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ArquivoNaoEncontrado(c) => {
                write!(f, "Arquivo nao encontrado: {}", c)
            }
            Self::LeituraFalhou(e) => write!(f, "Erro ao ler arquivo: {}", e),
            Self::FormatoDesconhecido(ext) => {
                write!(f, "Formato desconhecido: '{}'. Use .toml, .json ou .yaml", ext)
            }
            Self::ParseFalhou { formato, detalhe } => {
                write!(f, "Erro ao processar {} : {}", formato, detalhe)
            }
        }
    }
}

/// Detecta o formato do arquivo pela extensao
pub fn detectar_formato(caminho: &str) -> Result<Formato, ErroCarregamento> {
    let extensao = Path::new(caminho)
        .extension()
        .and_then(|e| e.to_str())
        .unwrap_or("");

    match extensao {
        "toml" => Ok(Formato::Toml),
        "json" => Ok(Formato::Json),
        "yaml" | "yml" => Ok(Formato::Yaml),
        outro => Err(ErroCarregamento::FormatoDesconhecido(outro.to_string())),
    }
}

/// Carrega a configuracao de um arquivo, aplicando valores padrao
pub fn carregar_de_arquivo(caminho: &str) -> Result<Configuracao, ErroCarregamento> {
    // Verifica se o arquivo existe
    if !Path::new(caminho).exists() {
        return Err(ErroCarregamento::ArquivoNaoEncontrado(caminho.to_string()));
    }

    // Le o conteudo do arquivo
    let conteudo = fs::read_to_string(caminho)
        .map_err(|e| ErroCarregamento::LeituraFalhou(e.to_string()))?;

    // Detecta o formato e faz o parse
    let formato = detectar_formato(caminho)?;
    let config = parse_conteudo(&conteudo, &formato)?;

    Ok(config)
}

/// Faz o parse do conteudo de acordo com o formato
fn parse_conteudo(
    conteudo: &str,
    formato: &Formato,
) -> Result<Configuracao, ErroCarregamento> {
    match formato {
        Formato::Toml => {
            toml::from_str(conteudo).map_err(|e| ErroCarregamento::ParseFalhou {
                formato: "TOML".to_string(),
                detalhe: e.to_string(),
            })
        }
        Formato::Json => {
            serde_json::from_str(conteudo).map_err(|e| ErroCarregamento::ParseFalhou {
                formato: "JSON".to_string(),
                detalhe: e.to_string(),
            })
        }
        Formato::Yaml => {
            serde_yaml::from_str(conteudo).map_err(|e| ErroCarregamento::ParseFalhou {
                formato: "YAML".to_string(),
                detalhe: e.to_string(),
            })
        }
    }
}

/// Aplica variaveis de ambiente como sobrescrita na configuracao.
/// Convencao: APP_SECAO_CAMPO (ex: APP_SERVIDOR_PORTA=9090)
pub fn aplicar_variaveis_ambiente(config: &mut Configuracao) {
    // Servidor
    if let Ok(val) = env::var("APP_NOME") {
        config.nome_app = val;
    }
    if let Ok(val) = env::var("APP_AMBIENTE") {
        config.ambiente = val;
    }
    if let Ok(val) = env::var("APP_SERVIDOR_HOST") {
        config.servidor.host = val;
    }
    if let Ok(val) = env::var("APP_SERVIDOR_PORTA") {
        if let Ok(porta) = val.parse::<u16>() {
            config.servidor.porta = porta;
        }
    }
    if let Ok(val) = env::var("APP_SERVIDOR_MAX_CONEXOES") {
        if let Ok(n) = val.parse::<u32>() {
            config.servidor.max_conexoes = n;
        }
    }
    if let Ok(val) = env::var("APP_SERVIDOR_TIMEOUT") {
        if let Ok(n) = val.parse::<u64>() {
            config.servidor.timeout_segundos = n;
        }
    }

    // Banco de dados
    if let Ok(val) = env::var("APP_BANCO_URL") {
        config.banco_de_dados.url = val;
    }
    if let Ok(val) = env::var("APP_BANCO_POOL_MAXIMO") {
        if let Ok(n) = val.parse::<u32>() {
            config.banco_de_dados.pool_maximo = n;
        }
    }
    if let Ok(val) = env::var("APP_BANCO_POOL_MINIMO") {
        if let Ok(n) = val.parse::<u32>() {
            config.banco_de_dados.pool_minimo = n;
        }
    }

    // Log
    if let Ok(val) = env::var("APP_LOG_NIVEL") {
        config.log.nivel = val;
    }
    if let Ok(val) = env::var("APP_LOG_FORMATO") {
        config.log.formato = val;
    }
    if let Ok(val) = env::var("APP_LOG_ARQUIVO") {
        config.log.arquivo = val;
    }

    // Seguranca
    if let Ok(val) = env::var("APP_SEGURANCA_CHAVE_SECRETA") {
        config.seguranca.chave_secreta = val;
    }
    if let Ok(val) = env::var("APP_SEGURANCA_EXPIRACAO_TOKEN") {
        if let Ok(n) = val.parse::<u32>() {
            config.seguranca.expiracao_token_horas = n;
        }
    }
    if let Ok(val) = env::var("APP_SEGURANCA_CORS_ORIGENS") {
        config.seguranca.cors_origens = val
            .split(',')
            .map(|s| s.trim().to_string())
            .collect();
    }
}

/// Converte a configuracao para o formato especificado
pub fn exportar(config: &Configuracao, formato: &Formato) -> Result<String, String> {
    match formato {
        Formato::Toml => {
            toml::to_string_pretty(config).map_err(|e| e.to_string())
        }
        Formato::Json => {
            serde_json::to_string_pretty(config).map_err(|e| e.to_string())
        }
        Formato::Yaml => {
            serde_yaml::to_string(config).map_err(|e| e.to_string())
        }
    }
}
```

A funcao `aplicar_variaveis_ambiente` segue a convencao `APP_SECAO_CAMPO` — um padrao muito comum em aplicacoes de producao (ex: Docker, Kubernetes). Variaveis numericas sao convertidas com `parse`, e valores invalidos sao silenciosamente ignorados, mantendo o valor original.

## Passo 3: Validacao da Configuracao

O modulo `validador.rs` verifica se a configuracao final e consistente e segura.

```rust
// src/validador.rs
use crate::config::Configuracao;

/// Resultado da validacao com lista de erros e avisos
pub struct ResultadoValidacao {
    pub erros: Vec<String>,
    pub avisos: Vec<String>,
}

impl ResultadoValidacao {
    fn nova() -> Self {
        Self {
            erros: Vec::new(),
            avisos: Vec::new(),
        }
    }

    /// Retorna true se nao ha erros (avisos sao permitidos)
    pub fn eh_valido(&self) -> bool {
        self.erros.is_empty()
    }
}

/// Valida a configuracao e retorna erros e avisos
pub fn validar(config: &Configuracao) -> ResultadoValidacao {
    let mut resultado = ResultadoValidacao::nova();

    // Validar nome da aplicacao
    if config.nome_app.trim().is_empty() {
        resultado.erros.push(
            "O nome da aplicacao nao pode ser vazio.".to_string(),
        );
    }

    // Validar ambiente
    let ambientes_validos = ["desenvolvimento", "producao", "teste", "homologacao"];
    if !ambientes_validos.contains(&config.ambiente.as_str()) {
        resultado.erros.push(format!(
            "Ambiente '{}' invalido. Use: {}",
            config.ambiente,
            ambientes_validos.join(", ")
        ));
    }

    // Validar servidor
    if config.servidor.porta == 0 {
        resultado.erros.push(
            "A porta do servidor nao pode ser 0.".to_string(),
        );
    }
    if config.servidor.porta < 1024 && config.servidor.porta != 0 {
        resultado.avisos.push(format!(
            "Porta {} requer privilegios de root.",
            config.servidor.porta
        ));
    }
    if config.servidor.max_conexoes == 0 {
        resultado.erros.push(
            "O numero maximo de conexoes deve ser maior que 0.".to_string(),
        );
    }
    if config.servidor.timeout_segundos == 0 {
        resultado.avisos.push(
            "Timeout de 0 segundos desabilita o timeout.".to_string(),
        );
    }

    // Validar banco de dados
    if config.banco_de_dados.url.trim().is_empty() {
        resultado.erros.push(
            "A URL do banco de dados nao pode ser vazia.".to_string(),
        );
    }
    if config.banco_de_dados.pool_minimo > config.banco_de_dados.pool_maximo {
        resultado.erros.push(format!(
            "Pool minimo ({}) nao pode ser maior que o maximo ({}).",
            config.banco_de_dados.pool_minimo, config.banco_de_dados.pool_maximo
        ));
    }

    // Validar log
    let niveis_validos = ["trace", "debug", "info", "warn", "error"];
    if !niveis_validos.contains(&config.log.nivel.as_str()) {
        resultado.erros.push(format!(
            "Nivel de log '{}' invalido. Use: {}",
            config.log.nivel,
            niveis_validos.join(", ")
        ));
    }
    let formatos_validos = ["texto", "json"];
    if !formatos_validos.contains(&config.log.formato.as_str()) {
        resultado.erros.push(format!(
            "Formato de log '{}' invalido. Use: {}",
            config.log.formato,
            formatos_validos.join(", ")
        ));
    }

    // Validar seguranca
    if config.ambiente == "producao"
        && config.seguranca.chave_secreta == "ALTERAR_EM_PRODUCAO"
    {
        resultado.erros.push(
            "Chave secreta padrao detectada em ambiente de producao! Defina uma chave segura.".to_string(),
        );
    }
    if config.seguranca.chave_secreta.len() < 16 {
        resultado.avisos.push(
            "A chave secreta tem menos de 16 caracteres. Considere usar uma chave mais longa.".to_string(),
        );
    }
    if config.seguranca.expiracao_token_horas == 0 {
        resultado.erros.push(
            "O tempo de expiracao do token deve ser maior que 0.".to_string(),
        );
    }
    if config.seguranca.cors_origens.is_empty() {
        resultado.avisos.push(
            "Nenhuma origem CORS configurada. Requisicoes cross-origin serao bloqueadas.".to_string(),
        );
    }

    resultado
}
```

A validacao distingue entre **erros** (problemas que impedem a execucao) e **avisos** (situacoes que merecem atencao mas nao sao fatais). Isso e especialmente util para detectar configuracoes inseguras em ambiente de producao.

## Passo 4: Juntando Tudo no main.rs

```rust
// src/main.rs
mod carregador;
mod config;
mod validador;

use carregador::Formato;
use clap::{Parser, Subcommand};
use colored::*;
use config::Configuracao;

#[derive(Parser)]
#[command(name = "config-manager")]
#[command(about = "Gerenciador de configuracoes multi-formato")]
struct Cli {
    #[command(subcommand)]
    comando: Comando,
}

#[derive(Subcommand)]
enum Comando {
    /// Carrega e exibe a configuracao de um arquivo
    Carregar {
        /// Caminho do arquivo de configuracao
        arquivo: String,

        /// Aplicar variaveis de ambiente como sobrescrita
        #[arg(short, long)]
        ambiente: bool,
    },

    /// Valida um arquivo de configuracao
    Validar {
        /// Caminho do arquivo de configuracao
        arquivo: String,
    },

    /// Gera um arquivo de configuracao com valores padrao
    Gerar {
        /// Formato de saida: toml, json ou yaml
        #[arg(short, long, default_value = "toml")]
        formato: String,

        /// Caminho do arquivo de saida (padrao: stdout)
        #[arg(short, long)]
        saida: Option<String>,
    },

    /// Converte um arquivo de configuracao para outro formato
    Converter {
        /// Arquivo de entrada
        entrada: String,

        /// Formato de saida: toml, json ou yaml
        #[arg(short, long)]
        formato: String,
    },
}

fn main() {
    let cli = Cli::parse();

    match cli.comando {
        Comando::Carregar { arquivo, ambiente } => {
            cmd_carregar(&arquivo, ambiente);
        }
        Comando::Validar { arquivo } => {
            cmd_validar(&arquivo);
        }
        Comando::Gerar { formato, saida } => {
            cmd_gerar(&formato, saida.as_deref());
        }
        Comando::Converter { entrada, formato } => {
            cmd_converter(&entrada, &formato);
        }
    }
}

fn cmd_carregar(arquivo: &str, aplicar_env: bool) {
    println!(
        "{} Carregando configuracao de '{}'...",
        "INFO:".blue().bold(),
        arquivo
    );

    let mut config = match carregador::carregar_de_arquivo(arquivo) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("{} {}", "ERRO:".red().bold(), e);
            std::process::exit(1);
        }
    };

    if aplicar_env {
        println!(
            "{} Aplicando variaveis de ambiente...",
            "INFO:".blue().bold()
        );
        carregador::aplicar_variaveis_ambiente(&mut config);
    }

    exibir_config(&config);
}

fn cmd_validar(arquivo: &str) {
    let config = match carregador::carregar_de_arquivo(arquivo) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("{} {}", "ERRO:".red().bold(), e);
            std::process::exit(1);
        }
    };

    let resultado = validador::validar(&config);

    if !resultado.avisos.is_empty() {
        println!("{}", "Avisos:".yellow().bold());
        for aviso in &resultado.avisos {
            println!("  {} {}", "AVISO:".yellow(), aviso);
        }
        println!();
    }

    if !resultado.erros.is_empty() {
        println!("{}", "Erros:".red().bold());
        for erro in &resultado.erros {
            println!("  {} {}", "ERRO:".red(), erro);
        }
        std::process::exit(1);
    }

    println!(
        "{} Configuracao valida! ({} aviso(s))",
        "OK".green().bold(),
        resultado.avisos.len()
    );
}

fn cmd_gerar(formato_str: &str, saida: Option<&str>) {
    let formato = match formato_str {
        "toml" => Formato::Toml,
        "json" => Formato::Json,
        "yaml" | "yml" => Formato::Yaml,
        _ => {
            eprintln!(
                "{} Formato '{}' nao suportado. Use: toml, json, yaml",
                "ERRO:".red().bold(),
                formato_str
            );
            std::process::exit(1);
        }
    };

    let config = Configuracao::default();
    let conteudo = match carregador::exportar(&config, &formato) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("{} {}", "ERRO:".red().bold(), e);
            std::process::exit(1);
        }
    };

    match saida {
        Some(caminho) => {
            if let Err(e) = std::fs::write(caminho, &conteudo) {
                eprintln!("{} {}", "ERRO:".red().bold(), e);
                std::process::exit(1);
            }
            println!(
                "{} Configuracao gerada em '{}'",
                "OK".green().bold(),
                caminho
            );
        }
        None => {
            println!("{}", conteudo);
        }
    }
}

fn cmd_converter(entrada: &str, formato_str: &str) {
    let formato_saida = match formato_str {
        "toml" => Formato::Toml,
        "json" => Formato::Json,
        "yaml" | "yml" => Formato::Yaml,
        _ => {
            eprintln!(
                "{} Formato '{}' nao suportado. Use: toml, json, yaml",
                "ERRO:".red().bold(),
                formato_str
            );
            std::process::exit(1);
        }
    };

    let config = match carregador::carregar_de_arquivo(entrada) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("{} {}", "ERRO:".red().bold(), e);
            std::process::exit(1);
        }
    };

    let conteudo = match carregador::exportar(&config, &formato_saida) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("{} {}", "ERRO:".red().bold(), e);
            std::process::exit(1);
        }
    };

    println!("{}", conteudo);
}

fn exibir_config(config: &Configuracao) {
    println!("\n{}", "=== Configuracao Carregada ===".bold().cyan());
    println!("  Nome:     {}", config.nome_app);
    println!("  Ambiente: {}", config.ambiente);
    println!();
    println!("{}", "  [servidor]".bold());
    println!("    host:            {}", config.servidor.host);
    println!("    porta:           {}", config.servidor.porta);
    println!("    max_conexoes:    {}", config.servidor.max_conexoes);
    println!("    timeout:         {}s", config.servidor.timeout_segundos);
    println!();
    println!("{}", "  [banco_de_dados]".bold());
    println!("    url:             {}", config.banco_de_dados.url);
    println!("    pool_maximo:     {}", config.banco_de_dados.pool_maximo);
    println!("    pool_minimo:     {}", config.banco_de_dados.pool_minimo);
    println!();
    println!("{}", "  [log]".bold());
    println!("    nivel:           {}", config.log.nivel);
    println!("    formato:         {}", config.log.formato);
    println!(
        "    arquivo:         {}",
        if config.log.arquivo.is_empty() {
            "(stdout)"
        } else {
            &config.log.arquivo
        }
    );
    println!();
    println!("{}", "  [seguranca]".bold());
    println!("    chave_secreta:   {}...", &config.seguranca.chave_secreta[..config.seguranca.chave_secreta.len().min(8)]);
    println!("    expiracao_token: {}h", config.seguranca.expiracao_token_horas);
    println!(
        "    cors_origens:    {}",
        config.seguranca.cors_origens.join(", ")
    );
}
```

## Como Executar

```bash
cargo build --release
```

Primeiro, gere um arquivo de configuracao de exemplo:

```bash
# Gerar configuracao padrao em TOML
./target/release/config-manager gerar --formato toml --saida config.toml

# Gerar em JSON
./target/release/config-manager gerar --formato json --saida config.json

# Gerar em YAML
./target/release/config-manager gerar --formato yaml --saida config.yaml
```

Exemplos de uso:

```bash
# Carregar e exibir configuracao
./target/release/config-manager carregar config.toml
# === Configuracao Carregada ===
#   Nome:     minha-app
#   Ambiente: desenvolvimento
#   [servidor]
#     host:            127.0.0.1
#     porta:           8080
#     ...

# Carregar com sobrescrita de variaveis de ambiente
APP_SERVIDOR_PORTA=9090 APP_AMBIENTE=producao \
  ./target/release/config-manager carregar config.toml --ambiente

# Validar configuracao
./target/release/config-manager validar config.toml
# OK Configuracao valida! (1 aviso(s))

# Converter de TOML para JSON
./target/release/config-manager converter config.toml --formato json

# Converter de JSON para YAML
./target/release/config-manager converter config.json --formato yaml
```

## Desafios para Expandir

1. **Suporte a profiles**: Implemente configuracoes por profile (ex: `config.producao.toml`, `config.teste.toml`) que herdam do arquivo base e sobrescrevem apenas os campos especificos do ambiente.

2. **Hot reload**: Adicione a capacidade de monitorar o arquivo de configuracao com `notify` e recarregar automaticamente quando ele for alterado, notificando a aplicacao via canal.

3. **Criptografia de segredos**: Implemente criptografia de campos sensiveis (chaves, senhas) no arquivo de configuracao usando a crate `aes-gcm`, com um comando `encrypt` e `decrypt` na CLI.

4. **Validacao com schema**: Crie um sistema de schema que descreva os tipos esperados, intervalos validos e dependencias entre campos, permitindo validacao generica de qualquer estrutura.

5. **Merge de multiplos arquivos**: Permita carregar e combinar configuracoes de varios arquivos em cascata (ex: `base.toml` + `local.toml`), onde arquivos posteriores sobrescrevem valores dos anteriores.

## Veja Tambem

- [HashMap: Tabelas Hash](/stdlib/hashmap/) — estrutura usada internamente pelo serde para mapas
- [Modulo env](/stdlib/env-module/) — acesso a variaveis de ambiente em Rust
- [Tipo Result](/stdlib/result/) — tratamento de erros no carregamento de configuracao
- [Lendo Arquivos TOML](/receitas/ler-toml/) — receita basica de leitura TOML
- [Variaveis de Ambiente](/receitas/variaveis-ambiente/) — trabalhando com env vars em Rust
