Recursively copies a directory from source to destination.
(
from: &'a Path,
to: &'a Path,
)
| 9 | |
| 10 | /// Recursively copies a directory from source to destination. |
| 11 | pub fn copy_dir<'a>( |
| 12 | from: &'a Path, |
| 13 | to: &'a Path, |
| 14 | ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<()>> + Send + 'a>> { |
| 15 | Box::pin(async move { |
| 16 | create_dir(to).await?; |
| 17 | |
| 18 | let mut entries = tokio::fs::read_dir(from).await?; |
| 19 | while let Some(entry) = entries.next_entry().await? { |
| 20 | let path = entry.path(); |
| 21 | let relative_path = path.strip_prefix(from)?; |
| 22 | let dst_path = to.join(relative_path); |
| 23 | |
| 24 | // Get metadata without following symlinks to check if it's a symlink |
| 25 | let metadata = tokio::fs::symlink_metadata(&path).await?; |
| 26 | |
| 27 | if metadata.is_symlink() { |
| 28 | // Preserve symlinks by reading the target and creating a new symlink |
| 29 | let target = tokio::fs::read_link(&path).await?; |
| 30 | if let Some(parent) = dst_path.parent() { |
| 31 | create_dir(parent).await?; |
| 32 | } |
| 33 | // Create symlink at destination pointing to the same target |
| 34 | #[cfg(unix)] |
| 35 | tokio::fs::symlink(&target, &dst_path).await?; |
| 36 | #[cfg(windows)] |
| 37 | { |
| 38 | // On Windows, we need to determine if it's a file or directory symlink |
| 39 | // Try to get the target metadata to determine the type |
| 40 | if let Ok(target_metadata) = tokio::fs::metadata(&path).await { |
| 41 | if target_metadata.is_dir() { |
| 42 | tokio::fs::symlink_dir(&target, &dst_path).await?; |
| 43 | } else { |
| 44 | tokio::fs::symlink_file(&target, &dst_path).await?; |
| 45 | } |
| 46 | } else { |
| 47 | // If we can't determine the type, try as a file symlink |
| 48 | tokio::fs::symlink_file(&target, &dst_path).await?; |
| 49 | } |
| 50 | } |
| 51 | } else if metadata.is_dir() { |
| 52 | copy_dir(&path, &dst_path).await?; |
| 53 | } else { |
| 54 | if let Some(parent) = dst_path.parent() { |
| 55 | create_dir(parent).await?; |
| 56 | } |
| 57 | copy_file(&path, &dst_path).await?; |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | Ok(()) |
| 62 | }) |
| 63 | } |
| 64 | |
| 65 | /// Writes content to a file, creating parent directories if needed. |
| 66 | pub async fn write_file(path: &Path, content: &str) -> Result<()> { |