use color_eyre::eyre::Result; use std::path::{Path, PathBuf}; use tokio::fs; use tokio::process::Command; use tokio_stream::wrappers::ReadDirStream; use futures::prelude::*; use serde::Deserialize; use crate::filters::Filter; /// Definition of the job files #[derive(Debug, Deserialize)] pub struct Job { filters: Vec>, location: PathBuf, actions: Vec, } impl Job { pub async fn run(&self) -> Result + '_> { let loc = normalise_path(&self.location); let dir = ReadDirStream::new(fs::read_dir(&loc).await?); Ok(async_stream::stream! { for await entry in dir { let entry = entry.unwrap(); if self.matches_filters(&entry.path()).await { for action in &self.actions { yield (action.clone(), entry.path()) } } } }) } async fn matches_filters(&self, path: &Path) -> bool { stream::iter(&self.filters).all(|f| f.matches(path)).await } } /// Actions available for file #[derive(Clone, Debug, Deserialize)] #[serde(untagged)] pub enum Action { /// Run given script with 1st argument. It will be ran in parent directory for given file Script { script: Box }, /// Move given file to new destination Move { move_to: Box }, /// Print message and do nothing Echo { message: String }, /// Move to trash Trash, } impl Action { pub async fn execute(self, source: PathBuf) { match self { Action::Script { ref script } => { Command::new(script.as_ref()) .arg(&source) .current_dir(source.parent().unwrap()) .spawn() .expect("Couldnt spawn process") .wait() .await .expect("Child exited abnormally"); } Action::Move { move_to: ref dest_dir, } => { let dest = normalise_path(dest_dir).join(source.file_name().unwrap()); if let Err(err) = fs::rename(&source, &dest).await { if err.raw_os_error() == Some(libc::EXDEV) { panic!("X dev"); } else { panic!("Cannot move {source:?} -> {dest:?}: {err:?}"); } } } Action::Echo { ref message } => println!("{source:?} - {message}"), Action::Trash => trash::delete(source).unwrap(), } } } fn normalise_path(path: &Path) -> PathBuf { match path.strip_prefix("~") { Ok(prefix) => std::env::home_dir().unwrap().join(prefix), Err(_) => path.to_owned(), } }