Un solo sitio donde se lanzan procesos
Todo lo de los dos niveles anteriores, junto y una sola vez: el helper que lanza, espera, mata y limpia, con todo lo que hay que hacer bien puesto por defecto.
Lo que el helper trae puesto
La forma
pub struct Lanzamiento { pub programa: Programa, // un enumerado, no una cadena. Ver abajo pub args: Vec<String>, pub limite: Duration, // con valor por omisión pub progreso: Option<Box<dyn Fn(Progreso) + Send>>,}pub struct Procesos { vivos: Mutex<HashMap<Uuid, Child>>,}impl Procesos { pub async fn ejecutar(&self, l: Lanzamiento) -> Result<Resultado, ErrorProceso> { /* … */ } pub async fn cancelar(&self, id: Uuid) -> bool { /* … */ } pub async fn matar_todos(&self) { /* al cerrar. Nivel 25 */ }}El detalle que más protege es el primer campo: programa no es una cadena sino un enumerado con los programas que tu aplicación puede lanzar.
pub enum Programa { Conversor, // el acompañante empaquetado Herramienta, // la del sistema, si está}Con eso, es imposible lanzar algo que no esté previsto, ni siquiera por error. La superficie deja de depender de que nadie escriba una cadena equivocada y pasa a estar en el sistema de tipos — que es la misma idea del nivel 14.4 aplicada dentro de tu propio código.
Impedir el otro camino
Un helper que se puede saltar no sirve de mucho, y hay dos formas de evitarlo:
- Que el módulo sea el único que importa lo necesario para lanzar, y el resto del código no tenga acceso. En Rust eso se consigue con la visibilidad de módulos.
- Y una comprobación automática que falle si aparece un lanzamiento fuera de ese módulo. Es una búsqueda de una línea en tu integración continua, y es lo que impide la deriva a los seis meses.
La segunda es la que de verdad funciona a largo plazo, porque la primera se salta con un pub que alguien añade sin pensarlo.
Y el registro
Una consecuencia agradable de tener un solo sitio: puedes registrar todos los lanzamientos con una línea, y eso da un dato que casi nadie tiene.
log::info!("proceso {:?} args={:?} → {} en {:?}", l.programa, l.args, codigo, duracion);Con eso, cuando un usuario reporte lentitud o un fallo raro, su registro dice qué se lanzó, con qué, cuánto tardó y qué devolvió. Es la diferencia entre diagnosticar y adivinar, y sale gratis por haber centralizado.