Skip to main content

serverust_core/
config.rs

1//! Carregamento de configuração via figment (serverust.toml + env vars).
2// figment::Error é grande por design (contém contexto de diagnóstico rico);
3// suprimir o lint é a escolha idiomática quando o tipo de erro é da API pública.
4#![allow(clippy::result_large_err)]
5
6use figment::providers::{Env, Format, Serialized, Toml};
7use figment::{Figment, Profile};
8use serde::{Deserialize, Serialize};
9
10/// Configuração do servidor HTTP local.
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct ServerConfig {
13    pub host: String,
14    pub port: u16,
15}
16
17impl Default for ServerConfig {
18    fn default() -> Self {
19        Self {
20            host: "127.0.0.1".to_string(),
21            port: 3000,
22        }
23    }
24}
25
26/// Configuração do runtime AWS Lambda.
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
28pub struct LambdaConfig {
29    pub memory_size: u32,
30    pub timeout_seconds: u32,
31}
32
33impl Default for LambdaConfig {
34    fn default() -> Self {
35        Self {
36            memory_size: 128,
37            timeout_seconds: 30,
38        }
39    }
40}
41
42/// Configuração de telemetria (logger + tracing).
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
44pub struct TelemetryConfig {
45    pub log_level: String,
46    pub format: String,
47}
48
49impl Default for TelemetryConfig {
50    fn default() -> Self {
51        Self {
52            log_level: "info".to_string(),
53            format: "json".to_string(),
54        }
55    }
56}
57
58/// Configuração de documentação OpenAPI.
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub struct OpenApiConfig {
61    pub title: String,
62    pub version: String,
63    pub docs_path: String,
64    pub redoc_path: String,
65}
66
67impl Default for OpenApiConfig {
68    fn default() -> Self {
69        Self {
70            title: "serverust".to_string(),
71            version: "0.1.0".to_string(),
72            docs_path: "/docs".to_string(),
73            redoc_path: "/redoc".to_string(),
74        }
75    }
76}
77
78/// Configuração raiz do projeto. Carregada de `serverust.toml` com override por env vars.
79///
80/// Formato do arquivo (profile-aware via figment):
81/// ```toml
82/// [default.server]
83/// host = "127.0.0.1"
84/// port = 3000
85///
86/// [default.lambda]
87/// memory_size = 128
88/// timeout_seconds = 30
89///
90/// [default.telemetry]
91/// log_level = "info"
92/// format = "json"
93///
94/// [default.openapi]
95/// title = "My API"
96/// version = "0.1.0"
97/// docs_path = "/docs"
98/// redoc_path = "/redoc"
99///
100/// # Overrides por perfil:
101/// [dev.server]
102/// port = 3001
103/// ```
104///
105/// Variáveis de ambiente sobrescrevem o arquivo (separador `__` para campos aninhados):
106/// - `SERVERUST_SERVER__PORT=8080`
107/// - `SERVERUST_TELEMETRY__LOG_LEVEL=debug`
108/// - `SERVERUST_PROFILE=prod` para selecionar perfil automaticamente
109#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
110pub struct ServerustConfig {
111    pub server: ServerConfig,
112    pub lambda: LambdaConfig,
113    pub telemetry: TelemetryConfig,
114    pub openapi: OpenApiConfig,
115}
116
117impl ServerustConfig {
118    /// Carrega de `serverust.toml` na raiz do projeto, perfil `default`.
119    /// Override por env vars com prefixo `SERVERUST_` (ex: `SERVERUST_SERVER__PORT=8080`).
120    pub fn load() -> Result<Self, figment::Error> {
121        Self::load_for_profile(Profile::Default)
122    }
123
124    /// Carrega de `serverust.toml` com perfil específico.
125    /// Perfil herda dados do `[default]` e os sobrescreve com `[<profile>]`.
126    /// O perfil também pode ser definido via `SERVERUST_PROFILE`.
127    pub fn load_for_profile(profile: impl Into<Profile>) -> Result<Self, figment::Error> {
128        Self::load_from_for_profile("serverust.toml", profile)
129    }
130
131    /// Carrega de arquivo específico no perfil `default`. Útil em testes.
132    pub fn load_from(path: &str) -> Result<Self, figment::Error> {
133        Self::load_from_for_profile(path, Profile::Default)
134    }
135
136    /// Carrega de arquivo específico com perfil específico. Útil em testes.
137    pub fn load_from_for_profile(
138        path: &str,
139        profile: impl Into<Profile>,
140    ) -> Result<Self, figment::Error> {
141        // .nested() faz top-level TOML keys virarem profile names:
142        // [default.server] → profile "default", key "server"
143        // [dev.server]     → profile "dev", key "server"
144        // Env::prefixed usa separador __ para campos aninhados:
145        // SERVERUST_SERVER__PORT → server.port
146        Figment::new()
147            .merge(Serialized::defaults(Self::default()))
148            .merge(Toml::file(path).nested())
149            .merge(Env::prefixed("SERVERUST_").split("__"))
150            .select(profile)
151            .extract()
152    }
153}