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
//! Copia **embebida** de la página de error del proyecto y de su hoja de estilos.
//!
//! Un binario instalado no lleva ficheros de datos: `cargo install errserve`
//! deja un ejecutable y nada más, y `CONTENT_DIR` apunta por defecto a un
//! `content` que no existe. Este módulo guarda los bytes de `content/` **dentro
//! del binario**, con `include_str!`/`include_bytes!` (decisión D3), de modo que
//! un binario instalado sirva la página de verdad y su hoja.
//!
//! Es **una** página —una plantilla con los marcadores `{{code}}`, `{{reason}}`
//! y `{{description}}`—, no once ficheros por código: la misma página sirve para
//! cualquier código de `400..=599`, rellenada al servir (`src/render.rs`).
//!
//! ---------------------------------------------------------------------------
//! API de la fase GREEN:
//!
//! ```ignore
//! /// La página HTML embebida del proyecto: la plantilla de `content/404.html`.
//! pub static PAGE: &'static str;
//!
//! /// Asset embebido por path **exacto** (`/css/error.css`), con su `Content-Type`.
//! pub fn asset(path: &str) -> Option<(&'static [u8], &'static str)>;
//! ```
//!
//! La consulta del asset compara el path **de forma exacta** —sin prefijos, sin
//! normalizar barras finales—, porque el respaldo de `ServeDir` recibe **todo
//! lo que no está en el disco** —`serve_dir/future.rs`, línea 108, manda al
//! respaldo lo mismo un `FileNotFound` que un `InvalidFilename`— y un `200` de
//! más ya no lo corrige nadie: con `fallback` nadie fuerza el estado.
//!
//! La prueba de paridad (decisión D5) vive **aquí**, en los `#[cfg(test)]` del
//! módulo que guarda la página, y **no** en `tests/`: el crate no tiene *lib
//! target*, así que una prueba de integración no ve las constantes del binario
//! —para verlas habría que añadir un `lib` que cambiaría la forma del crate para
//! todos—, y leer `content/` por `CARGO_MANIFEST_DIR` es lo que ya hace
//! `tests/content_pages.rs`.
/// La página HTML embebida del proyecto: los bytes de `content/404.html`.
///
/// Se llama `404.html` porque es la página del **código por defecto**
/// (`DEFAULT_ERROR_PAGE`, de fábrica `404`) y **conserva ese nombre**. El motivo
/// está **medido**: un fichero con nombre de código lo intercepta la cascada del
/// código **antes** de servirse, así que `GET /404.html` responde `404` con la
/// página y nunca `200`, mientras que un nombre neutro —`error.html`— se serviría
/// como asset **con los marcadores a la vista**. El precio, escrito: el fichero
/// se llama `404.html` y sirve también el `451`.
///
/// Es la **tercera** capa de la cascada de [`crate::render`]: se consulta solo
/// cuando el directorio del operador no tenía ni el fichero del código ni el del
/// código por defecto. Como cubre cualquier código de `400..=599`, la cascada no
/// puede quedarse sin página.
///
/// `include_str!` exige que el fichero exista al compilar (D6), de modo que
/// borrar o renombrar `content/404.html` deja de degradarse en silencio al
/// respaldo de texto y pasa a ser un error de compilación; el reverso —una página
/// nueva del directorio que nadie embebe— lo vigila la prueba de paridad (D5).
pub static PAGE: &str = include_str!;
/// Los bytes de `content/css/error.css`, como `static` **de tipo array**.
///
/// Un `static` tiene **una sola dirección**, garantizada por el lenguaje, y de
/// este se toman las dos referencias —la del asset y la del tema `default`— al
/// **mismo** array, así que apuntan a la misma dirección. Es lo que hace que
/// `std::ptr::eq` sobre [`DEFAULT_SHEET`] sea una comprobación **estable**.
///
/// El array intermedio **no es decorativo**: `static X: &[u8] =
/// include_bytes!(…)` no basta, porque al leer esa `&[u8]` en el inicializador
/// de otro `static` el compilador **inlinea** el `include_bytes!` de nuevo y
/// cada expansión crea un `static` anónimo propio. Con varias *codegen units*,
/// el enlazador no los funde y las direcciones difieren aunque los bytes sean
/// iguales (medido con `cargo test`, que compila el binario en varias CGU). Un
/// `static` de tipo array sí tiene una única dirección y `&DEFAULT_SHEET_BYTES`
/// la referencia en todos los sitios.
///
/// El fichero tiene que existir al compilar (`include_bytes!` lo exige, D6), y
/// `content/` viaja dentro del `.crate`, así que un binario instalado la sirve.
pub static DEFAULT_SHEET_BYTES: =
*include_bytes!;
/// La hoja de estilos de `content/css/error.css`, embebida **una sola vez**.
///
/// Es la **única** copia de esos bytes en el binario, y la comparten dos
/// consumidores que no pueden divergir: el asset `/css/error.css`
/// ([`ASSETS`]) y el tema `default` ([`crate::theme::THEMES`]). Si cada uno
/// llevara su propio `include_bytes!`, el binario guardaría los mismos bytes
/// dos veces y, peor, podrían separarse sin que nadie lo notase.
///
/// Apunta a [`DEFAULT_SHEET_BYTES`], un `static` de tipo array con **una sola
/// dirección**: las dos lecturas de este valor de puntero —la de [`ASSETS`] y la
/// de [`crate::theme::THEMES`]— dan la **misma** dirección, y la identidad se
/// comprueba con `std::ptr::eq` (ver la documentación de
/// [`DEFAULT_SHEET_BYTES`]). Con una `const`, o incluso con un `static` de tipo
/// `&[u8]` que lleve el `include_bytes!` directamente, la identidad **no** es
/// estable.
pub static DEFAULT_SHEET: & = &DEFAULT_SHEET_BYTES;
/// Assets embebidos, por su path **exacto**, con su `Content-Type`.
///
/// El path se compara tal cual —sin prefijos, sin normalizar barras finales—
/// porque el respaldo de `ServeDir` recibe **todo lo que no está en el disco**,
/// no solo un `FileNotFound`: `serve_dir/future.rs`, línea 108, hace caer al
/// respaldo lo mismo `FileNotFound` que `InvalidFilename` —que no es «un
/// directorio» sino el byte NUL, `serve_dir/open_file.rs`, líneas 117-118 y
/// 153-154, y un directorio sale como `FileNotFound`, `open_file.rs`, líneas
/// 290-291, con `append_index_html_on_directories(false)`— y, como el `fallback`
/// no fuerza el estado de la respuesta, un `200` de más
/// no lo corrige nadie (D2).
///
/// La hoja es [`DEFAULT_SHEET`]: el asset y el tema `default` son la **misma**
/// constante, no dos copias de los mismos bytes —y como [`DEFAULT_SHEET`] es un
/// `static`, comparten literalmente el mismo puntero (ver su documentación)—.
static ASSETS: = ;
/// Asset embebido por path **exacto** (`/css/error.css`), con su `Content-Type`.