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}