use crate::errors::{ConfGuardContext, ConfGuardError, ConfGuardResult};
use crate::util::link::create_link;
use derive_builder::Builder;
use derive_more::Display;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, Builder, Display, Default, Clone)]
#[display("MoveAndLink {{ source: {}, destination: {} }}", source_path.display(), destination_path.display())]
pub struct MoveAndLink {
source_path: PathBuf,
destination_path: PathBuf,
}
impl MoveAndLink {
pub fn new(
source_path: impl AsRef<Path>,
destination_path: impl AsRef<Path>,
) -> ConfGuardResult<Self> {
let current_dir = std::env::current_dir()?;
let source_ref = source_path.as_ref();
let source_abs = if source_ref.is_absolute() {
source_ref.to_path_buf()
} else {
current_dir.join(source_ref)
};
if !source_abs.exists() {
return Err(ConfGuardError::SourcePathNotFound(source_abs));
}
if source_abs.symlink_metadata()?.file_type().is_symlink() {
return Err(ConfGuardError::SourceIsSymlink(source_abs));
}
let source_canonical = source_abs
.canonicalize()
.with_context(|| format!("canonicalize source path: {:?}", source_abs))?;
let dest_ref = destination_path.as_ref();
let dest_abs = if dest_ref.is_absolute() {
dest_ref.to_path_buf()
} else {
current_dir.join(dest_ref)
};
Ok(Self {
source_path: source_canonical,
destination_path: dest_abs,
})
}
pub fn move_and_link(&self, relative: bool) -> ConfGuardResult<()> {
if let Some(parent) = self.destination_path.parent() {
fs::create_dir_all(parent)?;
}
fs::rename(&self.source_path, &self.destination_path).with_context(|| {
format!("move {:?} to {:?}", self.source_path, self.destination_path)
})?;
create_link(&self.destination_path, &self.source_path, relative)?;
Ok(())
}
pub fn revert(&self) -> ConfGuardResult<()> {
if !self.source_path.exists() {
return Err(ConfGuardError::SourceLinkNotFound(self.source_path.clone()));
}
if !self
.source_path
.symlink_metadata()?
.file_type()
.is_symlink()
{
return Err(ConfGuardError::SourceNotSymlink(self.source_path.clone()));
}
fs::remove_file(&self.source_path)?;
fs::rename(&self.destination_path, &self.source_path).with_context(|| {
format!(
"move {:?} back to {:?}",
self.destination_path, self.source_path
)
})?;
Ok(())
}
}