Saltar al contenido

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

Rust
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.

Rust
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.

Rust
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.