hice un programita en Rust para recordarme de ejercitar mis muñecas. simplemente hace una notificación y reproduce un sonidito (¡ding!) cada 20 minutos. es relativemente sencillo:

use clap::{App, Arg};
use notify_rust::Notification;
use rodio::Source;
use std::{
    convert::AsRef,
    io::{self, Read},
    sync::Arc,
    thread::sleep_ms,
};

// https://github.com/RustAudio/rodio/issues/141#issuecomment-383371609
pub struct Sound(Arc<Vec<u8>>);
impl AsRef<[u8]> for Sound {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl Sound {
    pub fn load(filename: &str) -> io::Result<Sound> {
        use std::fs::File;
        let mut buf = Vec::new();
        let mut file = File::open(filename)?;
        file.read_to_end(&mut buf)?;
        Ok(Sound(Arc::new(buf)))
    }
    pub fn cursor(self: &Self) -> io::Cursor<Sound> {
        io::Cursor::new(Sound(self.0.clone()))
    }
    pub fn decoder(self: &Self) -> rodio::Decoder<io::Cursor<Sound>> {
        rodio::Decoder::new(self.cursor()).unwrap()
    }
}

fn main() {
    let matches = App::new("Wristcare")
        .version("0.1")
        .author("void <v0id@riseup.net>")
        .about("Un daemon para cuidarte las muñecas")
        .arg(
            Arg::with_name("sound_path")
                .short('s')
                .long("sound")
                .value_name("SOUND_FILE_PATH")
                .help("Hace que suene un sonidito cuando se haga la notificación")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("time")
                .short('t')
                .long("time")
                .value_name("SECONDS")
                .help("El tiempo entre cada notificación en segundos. Por defecto: 20 minutos.")
                .takes_value(true),
        )
        .get_matches();

    let device = rodio::default_output_device().unwrap();
    let sound = match matches.value_of("sound_path") {
        Some(sound_path) => Some(Sound::load(sound_path).unwrap()),
        None => None,
    };

    let delay = matches.value_of_t("time").unwrap_or(60 * 20) * 1000;

    loop {
        sleep_ms(delay); // 20 minutos
        if let Some(source) = &sound {
            rodio::play_raw(&device, source.decoder().convert_samples());
        }
        Notification::new()
            .summary("Wristcare")
            .body("¡Es momento de ejercitar tus muñecas!")
            .show()
            .unwrap();
    }
}

(no hace falta que lo entiendas mucho :P)

resulta que no se que problema tiene este programa, que a veces lanza a usar 100% de CPU por ninguna razón.

después me di cuenta de que lo puedo reemplazar con un script… de 6 líneas (realmente son solo 5):

#!/bin/sh
while true; do
	sleep 1200
	mpv ~/.local/sound.mp3 &
	notify-send "¡Es momento de ejercitar tus muñecas!"
done

¿espera, khe? analizemos la situación. primero que todo, estos dos programas técnicamente no hacen lo mismo:

  • el de rust carga el sonido a la memoria en vez de leerlo cada vez que se reproduce. tuve que copiar código que no me molesté en entender para lograr esto.
  • el de rust parsea los parametros que le pasas (“–time --sound ") para funcionar en cualquier lado. el script probablemente lo podría hacer sin tanta complicación, pero ¿pra qué? si lo puedo modificar minimamente para modificar esos parametros.

técnicamente el de rust podría ser más sencillo, pero no lo es. y aún así, el script es más sencillo, sin ninguna desventaja muy grande (este script no esta haciendo nada constantemente, solo esperando la mayoría del tiempo, así que no es grave si usa un poco más de memoria o CPU, que de todas maneras no lo hace).

moraleja: fijate si puede ser un script, o si puede ser más sencillo. probablemente si.