Manejo de errores y logging — Backend
Contexto: Trabajo en este repo sigue el ROL de @CURSOR_AUDIT_PROMPT.md (líneas 3-4): Staff Engineer / Arquitecto senior (Angular + Ionic + PHP + Rust), experto en arquitectura hexagonal, testing y sistemas multi-app.
Prohibición de panic: unwrap y expect
- Queda prohibido usar
.unwrap()y.expect()en código que atienda requests (handlers, repos, servicios llamados desde handlers). Ambos pueden provocar panic y tumbar el proceso. - Sí permitido:
.unwrap_or(),.unwrap_or_else(),.unwrap_or_default()— no paniquean, dan un valor por defecto. - Excepción: dentro de
#[cfg(test)](tests unitarios) se puede usar.unwrap()/.expect()cuando el test controla los datos; aun así preferir?o afirmaciones si el fallo es posible.
❌ Prohibido (riesgo de panic)
let url = state.config.photo_to_full_url(Some(&hash)).unwrap();
let json = serde_json::to_string(&event).unwrap();
let t = NaiveTime::from_hms_opt(h, m, 0).unwrap(); // en código de producción
let url = state.config.photo_to_full_url(Some(&hash)).unwrap_or_else(|| hash.clone());
let json = serde_json::to_string(&event).unwrap_or_default();
let t = NaiveTime::from_hms_opt(h, m, 0).ok_or(AppError::BadRequest("hora inválida".into()))?;
- Cualquier error recuperable debe convertirse en
AppErrory devolverse (o loguearse conlog_internaly devolverAppError::Internal). No ocultar fallos conunwrap().
Tipo de error unificado
- Usar siempre
crate::error::AppErrorpara respuestas HTTP. Errores internos (BD, IO, serialización): usarlog_internal("contexto", e)?para registrar la cadena de causas y convertir aAppError::Internal.
Tracing obligatorio
- En cada handler que realice una operación relevante (crear/actualizar/borrar, login, clock-in, etc.) debe haber al menos un
tracing::infootracing::debugcon contexto (place_id, user_id, acción). Así se puede seguir el flujo y diagnosticar sin depender solo del error HTTP. - Errores:
log_internaly elIntoResponsedeAppErrorya registran internals; en handlers se puede añadir un log explícito cuando el contexto ayude (ej. parámetros del request que falló).
❌ Prohibido
✅ Correcto