TODO: Preserve file ext Fortune for lazy-rebuild's current commit: Curse − 凶rust-version
parent
24c0fcb584
commit
40bfa1d01a
@ -0,0 +1,107 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
use futures::{
|
||||||
|
Future,
|
||||||
|
Stream, StreamExt,
|
||||||
|
};
|
||||||
|
use tokio::io::{
|
||||||
|
AsyncReadExt,
|
||||||
|
AsyncWriteExt,
|
||||||
|
};
|
||||||
|
use tokio::sync::{
|
||||||
|
mpsc,
|
||||||
|
};
|
||||||
|
use std::io;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use tokio::fs;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
handle_file: mpsc::Sender<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_file(state: State, file: PathBuf) -> io::Result<()>
|
||||||
|
{
|
||||||
|
debug_assert!(file.is_file());
|
||||||
|
|
||||||
|
state.handle_file.send(file).await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)] fn handle_dir2(state: State, dir: PathBuf) -> futures::future::BoxFuture<'static, io::Result<()>>
|
||||||
|
{
|
||||||
|
use futures::prelude::*;
|
||||||
|
|
||||||
|
handle_dir(state, dir).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_dir(state: State, dir: impl AsRef<Path>) -> io::Result<()>
|
||||||
|
{
|
||||||
|
debug_assert!(dir.as_ref().is_dir());
|
||||||
|
let mut read = fs::read_dir(dir).await?;
|
||||||
|
while let Some(item) = read.next_entry().await?
|
||||||
|
{
|
||||||
|
let path = item.path();
|
||||||
|
if path.is_file() {
|
||||||
|
tokio::spawn(handle_file(state.clone(), path));
|
||||||
|
} else {
|
||||||
|
tokio::spawn(handle_dir2(state.clone(), path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start<I, T>(bases: I, cancel: impl Future<Output=()> + Send + 'static) -> io::Result<()>
|
||||||
|
where I: IntoIterator<Item = T>,
|
||||||
|
T: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let (tx, rx) = handle::spawn_with_cancel(Default::default(), cancel);
|
||||||
|
let renamer = tokio::spawn(async move {
|
||||||
|
use futures::prelude::*;
|
||||||
|
rx.for_each_concurrent(4, |(path, hash)| async move
|
||||||
|
{
|
||||||
|
match hash {
|
||||||
|
Ok(hash) => {
|
||||||
|
let new_name = path.parent().unwrap().join(hash.to_hex_string());
|
||||||
|
if let Err(_) = tokio::spawn(async move {
|
||||||
|
match fs::rename(&path, &new_name).await
|
||||||
|
{
|
||||||
|
Ok(_) => println!("[.] {:?} -> {:?}", path, new_name),
|
||||||
|
Err(err) => println!("[!] {:?}: {}", path, err),
|
||||||
|
}
|
||||||
|
}).await {
|
||||||
|
eprintln!("[!] panic: rename");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("[!] {:?}: {}", path, err);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
let res = 'result: loop {
|
||||||
|
let state = State { handle_file: tx};
|
||||||
|
let res = futures::future::join_all(bases.into_iter().map(|base| {
|
||||||
|
use futures::prelude::*;
|
||||||
|
if base.as_ref().is_dir() {
|
||||||
|
handle_dir(state.clone(), base).boxed_local()
|
||||||
|
} else {
|
||||||
|
handle_file(state.clone(), base.as_ref().to_owned()).boxed_local()
|
||||||
|
}
|
||||||
|
})).await;
|
||||||
|
for res in res {
|
||||||
|
match res {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => break 'result Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(renamer.await.is_ok(), "[!] fatal: renamer task panic");
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
Loading…
Reference in new issue