1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
//! Carga las opciones de configuración de la aplicación.
//!
//! Estos ajustes se obtienen de archivos [TOML](https://toml.io) como pares `clave = valor` que se
//! mapean a estructuras **fuertemente tipadas** y valores predefinidos.
//!
//! Siguiendo la metodología [Twelve-Factor App](https://12factor.net/config), PageTop separa el
//! **código** de la **configuración**, lo que permite tener configuraciones diferentes para cada
//! despliegue, como *dev*, *staging* o *production*, sin modificar el código fuente.
//!
//!
//! # Orden de carga
//!
//! Si tu aplicación necesita archivos de configuración, crea un directorio `config` en la raíz del
//! proyecto, al mismo nivel que el archivo *Cargo.toml* o que el binario de la aplicación.
//!
//! PageTop carga en este orden, y siempre de forma opcional, los siguientes archivos TOML:
//!
//! 1. **config/common.toml**, para ajustes comunes a todos los entornos. Este enfoque simplifica el
//! mantenimiento al centralizar los valores de configuración comunes.
//!
//! 2. **config/{rm}.toml**, donde `{rm}` es el valor de la variable de entorno `PAGETOP_RUN_MODE`:
//!
//! * Si `PAGETOP_RUN_MODE` no está definida, se asume el valor `default`, y PageTop intentará
//! cargar *config/default.toml* si el archivo existe.
//!
//! * Permite definir configuraciones específicas por entorno, garantizando que cada uno (p. ej.,
//! *dev*, *staging* o *production*) disponga de sus propias opciones, como claves de API, URLs
//! o ajustes de rendimiento, sin afectar a los demás.
//!
//! 3. **config/local.{rm}.toml**, útil para configuraciones locales específicas de la máquina o de
//! la ejecución:
//!
//! * Permite añadir o sobrescribir ajustes propios del entorno. Por ejemplo, `local.dev.toml`
//! para desarrollo o `local.production.toml` para retoques en producción.
//!
//! * Facilita que cada desarrollador adapte la configuración a su equipo en un entorno dado. Por
//! lo general no se comparte ni se sube al sistema de control de versiones.
//!
//! 4. **config/local.toml**, para ajustes locales válidos en cualquier entorno, ideal para cambios
//! rápidos o valores temporales que no dependan de un entorno concreto.
//!
//! Los archivos se combinan en el orden anterior, cada archivo sobrescribe a los anteriores en caso
//! de conflicto.
//!
//!
//! # Cómo añadir opciones de configuración a tu código
//!
//! Añade [*serde*](https://docs.rs/serde) en tu archivo *Cargo.toml* con la *feature* `derive`:
//!
//! ```toml
//! [dependencies]
//! serde = { version = "1.0", features = ["derive"] }
//! ```
//!
//! Y usa la macro [`include_config!`](crate::include_config) para inicializar tus ajustes en una
//! estructura con tipos seguros. Por ejemplo:
//!
//! ```rust,no_run
//! use pagetop::prelude::*;
//! use serde::Deserialize;
//!
//! include_config!(SETTINGS: Settings => [
//! // [myapp]
//! "myapp.name" => "Value Name",
//! "myapp.width" => 900,
//! "myapp.height" => 320,
//! ]);
//!
//! #[derive(Debug, Deserialize)]
//! pub struct Settings {
//! pub myapp: MyApp,
//! }
//!
//! #[derive(Debug, Deserialize)]
//! pub struct MyApp {
//! pub name: String,
//! pub description: Option<String>,
//! pub width: u16,
//! pub height: u16,
//! }
//! ```
//!
//! De esta forma estás añadiendo una nueva sección `[myapp]` a la configuración, igual que existen
//! `[app]` o `[server]` en las opciones globales de [`Settings`](crate::global::Settings).
//!
//! Se recomienda proporcionar siempre valores por defecto o usar `Option<T>` para los ajustes
//! opcionales.
//!
//! Si la configuración no se inicializa correctamente, la aplicación lanzará *panic* y detendrá la
//! ejecución.
//!
//! Las estructuras de configuración son de **sólo lectura** durante la ejecución.
//!
//!
//! # Usando tus opciones de configuración
//!
//! ```rust,ignore
//! use pagetop::prelude::*;
//! use crate::config;
//!
//! fn global_settings() {
//! println!("Nombre de la app: {}", &global::SETTINGS.app.name);
//! println!("Descripción: {}", &global::SETTINGS.app.description);
//! println!("Run mode: {}", &global::SETTINGS.app.run_mode);
//! }
//!
//! fn extension_settings() {
//! println!("{} - {:?}", &config::SETTINGS.myapp.name, &config::SETTINGS.myapp.description);
//! println!("{}", &config::SETTINGS.myapp.width);
//! }
//! ```
use crateutil;
use DefaultState;
use ;
use env;
use PathBuf;
use LazyLock;
// Nombre del directorio de configuración por defecto.
const DEFAULT_CONFIG_DIR: &str = "config";
// Modo de ejecución por defecto.
const DEFAULT_RUN_MODE: &str = "default";
/// Valores originales de los archivos de configuración como pares `clave = valor`.
pub static CONFIG_VALUES: = new;
// **< include_config! >****************************************************************************
/// Incluye los ajustes necesarios de la configuración anticipando valores por defecto.
///
/// # Sintaxis
///
/// Hay que añadir en nuestra librería el siguiente código:
///
/// ```rust,ignore
/// include_config!(SETTINGS: Settings => [
/// "ruta.clave" => valor,
/// // ...
/// ]);
/// ```
///
/// donde:
///
/// * **`SETTINGS_NAME`** es el nombre de la variable global que se usará para referenciar los
/// ajustes. Se recomienda usar `SETTINGS`, aunque no es obligatorio.
/// * **`Settings_Type`** es la referencia a la estructura que define los tipos para deserializar la
/// configuración. Debe implementar `Deserialize` (derivable con `#[derive(Deserialize)]`).
/// * **Lista de pares** con las claves TOML que requieran valores por defecto. Siguen la notación
/// `"seccion.subclave"` para coincidir con el árbol TOML.
///
/// # Ejemplo básico
///
/// ```rust,no_run
/// use pagetop::prelude::*;
/// use serde::Deserialize;
///
/// include_config!(SETTINGS: BlogSettings => [
/// // [blog]
/// "blog.title" => "Mi Blog",
/// "blog.port" => 8080,
/// ]);
///
/// #[derive(Debug, Deserialize)]
/// pub struct BlogSettings {
/// pub blog: Blog,
/// }
///
/// #[derive(Debug, Deserialize)]
/// pub struct Blog {
/// pub title: String,
/// pub description: Option<String>,
/// pub port: u16,
/// }
///
/// fn print_title() {
/// // Lectura en tiempo de ejecución.
/// println!("Título: {}", SETTINGS.blog.title);
/// }
/// ```
///
/// # Buenas prácticas
///
/// * **Valores por defecto**. Declara un valor por defecto para cada clave obligatoria. Las claves
/// opcionales pueden ser `Option<T>`.
///
/// * **Secciones únicas**. Agrupa tus claves dentro de una sección exclusiva (p. ej. `[blog]`) para
/// evitar colisiones con otras librerías.
///
/// * **Solo lectura**. La variable generada es inmutable durante toda la vida del programa. Para
/// configurar distintos entornos (*dev*, *staging*, *prod*) usa los archivos TOML descritos en la
/// documentación de [`config`](crate::config).
///
/// * **Errores explícitos**. Si la deserialización falla, la macro lanzará un `panic!` con un
/// mensaje que indica la estructura problemática, facilitando la depuración.
///
/// # Requisitos
///
/// * Dependencia `serde` con la *feature* `derive`.
/// * Las claves deben coincidir con los campos (*snake case*) de tu estructura `Settings_Type`.
///
/// ```toml
/// [dependencies]
/// serde = { version = "1.0", features = ["derive"] }
/// ```