use crate::error::{GitWarpError, Result};
use std::path::Path;
pub fn is_cow_supported<P: AsRef<Path>>(path: P) -> Result<bool> {
#[cfg(target_os = "macos")]
{
is_apfs(path)
}
#[cfg(target_os = "linux")]
{
Ok(false)
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
{
Ok(false)
}
}
pub fn clone_directory<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dest: Q) -> Result<()> {
let src = src.as_ref();
let dest = dest.as_ref();
if !src.exists() {
return Err(GitWarpError::WorktreeNotFound {
path: src.display().to_string(),
}.into());
}
#[cfg(target_os = "macos")]
{
clone_directory_apfs(src, dest)
}
#[cfg(target_os = "linux")]
{
Err(GitWarpError::CoWNotSupported.into())
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
{
Err(GitWarpError::CoWNotSupported.into())
}
}
#[cfg(target_os = "macos")]
fn is_apfs<P: AsRef<Path>>(path: P) -> Result<bool> {
use nix::sys::statfs::statfs;
let statfs = statfs(path.as_ref()).map_err(|e| {
anyhow::anyhow!("Failed to check filesystem: {}", e)
})?;
let fs_type = statfs.filesystem_type_name();
Ok(fs_type == "apfs")
}
#[cfg(target_os = "macos")]
fn clone_directory_apfs<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dest: Q) -> Result<()> {
use std::process::Command;
if !is_apfs(&src)? {
return Err(GitWarpError::CoWNotSupported.into());
}
if dest.as_ref().exists() {
std::fs::remove_dir_all(&dest)?;
}
let output = Command::new("cp")
.arg("-c") .arg("-R") .arg(src.as_ref())
.arg(dest.as_ref())
.output()
.map_err(|e| anyhow::anyhow!("Failed to execute cp command: {}", e))?;
if !output.status.success() {
let error = String::from_utf8_lossy(&output.stderr);
return Err(anyhow::anyhow!("Failed to clone directory with CoW: {}", error).into());
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
use std::fs;
#[test]
#[cfg(target_os = "macos")]
fn test_cow_support_check() {
let result = is_cow_supported(".");
assert!(result.is_ok());
}
#[test]
#[cfg(target_os = "macos")]
fn test_cow_clone() {
let temp_dir = tempdir().unwrap();
let src_dir = temp_dir.path().join("src");
let dest_dir = temp_dir.path().join("dest");
fs::create_dir(&src_dir).unwrap();
fs::write(src_dir.join("test.txt"), "Hello, World!").unwrap();
if is_cow_supported(&src_dir).unwrap_or(false) {
let result = clone_directory(&src_dir, &dest_dir);
assert!(result.is_ok());
assert!(dest_dir.join("test.txt").exists());
let content = fs::read_to_string(dest_dir.join("test.txt")).unwrap();
assert_eq!(content, "Hello, World!");
}
}
}