const PAGES: [(u16, &str); 11] = [
(400, include_str!("../content/400.html")),
(401, include_str!("../content/401.html")),
(403, include_str!("../content/403.html")),
(404, include_str!("../content/404.html")),
(405, include_str!("../content/405.html")),
(408, include_str!("../content/408.html")),
(429, include_str!("../content/429.html")),
(500, include_str!("../content/500.html")),
(502, include_str!("../content/502.html")),
(503, include_str!("../content/503.html")),
(504, include_str!("../content/504.html")),
];
pub const PAGE_CODES: [u16; 11] = [
PAGES[0].0,
PAGES[1].0,
PAGES[2].0,
PAGES[3].0,
PAGES[4].0,
PAGES[5].0,
PAGES[6].0,
PAGES[7].0,
PAGES[8].0,
PAGES[9].0,
PAGES[10].0,
];
pub fn page(code: u16) -> Option<&'static str> {
PAGE_CODES
.iter()
.position(|listed| *listed == code)
.and_then(|index| PAGES.get(index))
.map(|(_, html)| *html)
}
const ASSETS: [(&str, &[u8], &str); 1] = [(
"/css/error.css",
include_bytes!("../content/css/error.css"),
"text/css",
)];
pub fn asset(path: &str) -> Option<(&'static [u8], &'static str)> {
ASSETS
.iter()
.find(|(candidate, _, _)| *candidate == path)
.map(|(_, bytes, content_type)| (*bytes, *content_type))
}
#[cfg(test)]
mod tests {
use super::{PAGE_CODES, asset, page};
use std::path::PathBuf;
fn content_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("content")
}
const CODIGOS: [u16; 11] = [400, 401, 403, 404, 405, 408, 429, 500, 502, 503, 504];
#[test]
fn la_tabla_declara_los_once_codigos_con_pagina() {
assert_eq!(
PAGE_CODES.len(),
CODIGOS.len(),
"`PAGE_CODES` tiene que tener los once códigos con página"
);
for code in CODIGOS {
assert!(
page(code).is_some(),
"`page({code})` tiene que existir: es uno de los once códigos con página"
);
}
}
#[test]
fn cada_pagina_embebida_es_byte_a_byte_la_de_content() {
for code in PAGE_CODES {
let embebida = page(code)
.unwrap_or_else(|| panic!("`page({code})` debe existir: está en `PAGE_CODES`"));
let ruta = content_dir().join(format!("{code}.html"));
let del_disco = std::fs::read(&ruta)
.unwrap_or_else(|error| panic!("no se pudo leer {}: {error}", ruta.display()));
assert_eq!(
embebida.as_bytes(),
del_disco.as_slice(),
"la página embebida del `{code}` no coincide byte a byte con {}",
ruta.display()
);
}
}
#[test]
fn la_hoja_embebida_es_byte_a_byte_la_de_content() {
let (bytes, content_type) = asset("/css/error.css")
.expect("la hoja `/css/error.css` tiene que estar en la tabla de assets");
assert_eq!(content_type, "text/css", "Content-Type de la hoja embebida");
let ruta = content_dir().join("css/error.css");
let del_disco = std::fs::read(&ruta)
.unwrap_or_else(|error| panic!("no se pudo leer {}: {error}", ruta.display()));
assert_eq!(
bytes,
del_disco.as_slice(),
"la hoja embebida no coincide byte a byte con {}",
ruta.display()
);
}
#[test]
fn la_lista_de_codigos_y_el_directorio_dicen_lo_mismo() {
let entradas = std::fs::read_dir(content_dir())
.expect("el directorio `content/` tiene que poder listarse");
let mut codigos_del_directorio: Vec<u16> = entradas
.filter_map(Result::ok)
.map(|entrada| entrada.path())
.filter(|ruta| ruta.extension().is_some_and(|ext| ext == "html"))
.filter_map(|ruta| ruta.file_stem()?.to_str()?.parse::<u16>().ok())
.collect();
codigos_del_directorio.sort_unstable();
let mut codigos_de_la_tabla: Vec<u16> = PAGE_CODES.to_vec();
codigos_de_la_tabla.sort_unstable();
assert_eq!(
codigos_de_la_tabla, codigos_del_directorio,
"todo `*.html` de `content/` tiene que tener entrada en la tabla, y toda entrada \
de `PAGE_CODES` su fichero en `content/`"
);
for code in PAGE_CODES {
let ruta = content_dir().join(format!("{code}.html"));
assert!(
ruta.is_file(),
"`PAGE_CODES` incluye el `{code}`, pero no existe {}",
ruta.display()
);
}
}
}