Skip to content

Instantly share code, notes, and snippets.

@AyAyEm
Created August 14, 2022 19:07
Show Gist options
  • Save AyAyEm/50dad1580c8fad62978950960b6d01f6 to your computer and use it in GitHub Desktop.
Save AyAyEm/50dad1580c8fad62978950960b6d01f6 to your computer and use it in GitHub Desktop.
Non recursive async directory copy
use std::{collections::VecDeque, path::PathBuf};
use tokio::io;
#[derive(Debug, Clone, Copy)]
pub struct CopyOptions {
pub overwrite: bool,
}
impl Default for CopyOptions {
fn default() -> Self {
Self { overwrite: false }
}
}
pub async fn dir<F, T>(from: F, to: T, options: &CopyOptions) -> io::Result<()>
where
F: Into<PathBuf>,
T: Into<PathBuf>,
{
let from: PathBuf = from.into();
let to: PathBuf = to.into();
let component_count = from.components().count();
let to_path = |from_path: &PathBuf| {
let from_path = from_path
.components()
.skip(component_count)
.collect::<PathBuf>();
to.join(from_path)
};
let mut futures = vec![];
let mut directories = VecDeque::from([from]);
while let Some(dir) = directories.pop_front() {
let mut read_dir = tokio::fs::read_dir(dir).await?;
while let Ok(Some(entry)) = read_dir.next_entry().await {
let file_type = entry.file_type().await?;
let path = entry.path();
if file_type.is_dir() {
directories.push_back(path);
} else {
futures.push(async {
let copy_path = to_path(&path);
if let Some(parent) = copy_path.parent() {
match tokio::fs::create_dir_all(parent).await {
Err(err) => match err.kind() {
io::ErrorKind::AlreadyExists => Ok(()),
_ => Err(err),
},
_ => Ok(()),
}?;
}
match (copy_path.try_exists(), options.overwrite) {
(Ok(false), _) | (Ok(true), true) => {
tokio::fs::copy(path, copy_path).await
}
_ => Ok(0),
}
});
}
}
}
futures_util::future::try_join_all(futures).await?;
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment