use std::sync::Arc;
use rustic_core::{Progress, ProgressBars, ProgressType, RusticProgress};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AvisoDeProgreso {
TotalDeBytes(u64),
Bytes(u64),
FinDeBytes,
}
pub struct BarrasDelMotor {
al_avisar: Arc<dyn Fn(AvisoDeProgreso) + Send + Sync>,
}
impl BarrasDelMotor {
pub fn nuevas(al_avisar: Arc<dyn Fn(AvisoDeProgreso) + Send + Sync>) -> Self {
Self { al_avisar }
}
pub fn mudas() -> Self {
Self::nuevas(Arc::new(|_aviso| {}))
}
}
impl std::fmt::Debug for BarrasDelMotor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BarrasDelMotor").finish_non_exhaustive()
}
}
impl ProgressBars for BarrasDelMotor {
fn progress(&self, tipo: ProgressType, _prefijo: &str) -> Progress {
Progress::new(BarraDelMotor {
tipo,
al_avisar: Arc::clone(&self.al_avisar),
})
}
}
struct BarraDelMotor {
tipo: ProgressType,
al_avisar: Arc<dyn Fn(AvisoDeProgreso) + Send + Sync>,
}
impl std::fmt::Debug for BarraDelMotor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BarraDelMotor")
.field("tipo", &self.tipo)
.finish_non_exhaustive()
}
}
impl RusticProgress for BarraDelMotor {
fn is_hidden(&self) -> bool {
false
}
fn set_length(&self, longitud: u64) {
if matches!(self.tipo, ProgressType::Bytes) {
(self.al_avisar)(AvisoDeProgreso::TotalDeBytes(longitud));
}
}
fn set_title(&self, _titulo: &str) {}
fn inc(&self, incremento: u64) {
if matches!(self.tipo, ProgressType::Bytes) {
(self.al_avisar)(AvisoDeProgreso::Bytes(incremento));
}
}
fn finish(&self) {
if matches!(self.tipo, ProgressType::Bytes) {
(self.al_avisar)(AvisoDeProgreso::FinDeBytes);
}
}
}
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use rustic_core::{ProgressBars, ProgressType};
use super::{AvisoDeProgreso, BarrasDelMotor};
#[test]
fn las_barras_traducen_los_avisos_de_bytes() {
let recogidos = Arc::new(Mutex::new(Vec::new()));
let destino = Arc::clone(&recogidos);
let barras = BarrasDelMotor::nuevas(Arc::new(move |aviso: AvisoDeProgreso| {
destino
.lock()
.expect("el candado de la prueba no puede estar envenenado")
.push(aviso);
}));
let progreso = barras.progress(ProgressType::Bytes, "volcado");
assert!(
!progreso.is_hidden(),
"las barras del motor no están ocultas: el motor quiere el aviso"
);
progreso.set_length(2048);
progreso.set_title("volcado/tienda.sql");
progreso.inc(512);
progreso.finish();
let avisos = recogidos
.lock()
.expect("el candado de la prueba no puede estar envenenado")
.clone();
assert_eq!(
avisos,
vec![
AvisoDeProgreso::TotalDeBytes(2048),
AvisoDeProgreso::Bytes(512),
AvisoDeProgreso::FinDeBytes,
],
"los avisos no son los tres que el motor espera, o el título se convirtió en uno"
);
}
#[test]
fn un_contador_no_produce_ningun_aviso() {
let recogidos = Arc::new(Mutex::new(Vec::new()));
let destino = Arc::clone(&recogidos);
let barras = BarrasDelMotor::nuevas(Arc::new(move |aviso: AvisoDeProgreso| {
destino
.lock()
.expect("el candado de la prueba no puede estar envenenado")
.push(aviso);
}));
let contador = barras.progress(ProgressType::Counter, "getting snapshots...");
contador.set_length(7);
contador.set_title("volcado/tienda.sql");
contador.inc(1);
contador.finish();
assert!(
recogidos
.lock()
.expect("el candado de la prueba no puede estar envenenado")
.is_empty(),
"un `Counter` mandó avisos: sus incrementos de snapshot llegarían al motor \
mezclados con los bytes, y `bytes_uploaded` saldría falso sin que nada lo delatara"
);
let de_bytes = barras.progress(ProgressType::Bytes, "backing up...");
de_bytes.set_length(1024);
de_bytes.inc(256);
de_bytes.finish();
assert_eq!(
*recogidos
.lock()
.expect("el candado de la prueba no puede estar envenenado"),
vec![
AvisoDeProgreso::TotalDeBytes(1024),
AvisoDeProgreso::Bytes(256),
AvisoDeProgreso::FinDeBytes,
],
"el mismo destino tampoco dejó pasar los bytes: la comprobación del contador se hizo \
contra un canal roto y no mide nada"
);
}
#[test]
fn el_debug_de_las_barras_no_publica_el_callback() {
let distinguible = String::from("cadena-distinguible-del-callback-0001");
let capturada = distinguible.clone();
let barras = BarrasDelMotor::nuevas(Arc::new(move |_aviso: AvisoDeProgreso| {
let _ = capturada.len();
}));
let debug = format!("{barras:?}");
assert!(
debug.contains("BarrasDelMotor"),
"el Debug no dice ni de qué tipo es: {debug}"
);
assert!(
!debug.contains(&distinguible),
"el Debug de las barras publicó el contenido del callback: {debug}"
);
let progreso = barras.progress(ProgressType::Bytes, "volcado");
let debug_del_progreso = format!("{progreso:?}");
assert!(
!debug_del_progreso.contains(&distinguible),
"el Debug de la barra publicó el contenido del callback: {debug_del_progreso}"
);
}
}