use std::sync::Arc;
use super::abs_fs::{self, FsError};
use crate::model::{AbsolutePath, Command, CommandKind, SourcePath};
#[derive(Debug)]
pub enum ExecutionLog {
DirectoryCreated(Arc<AbsolutePath>),
PathDeleted(Arc<AbsolutePath>),
SymlinkCreated {
source_path: SourcePath,
destination_path: Arc<AbsolutePath>,
},
SymlinkRecreated {
source_path: SourcePath,
destination_path: Arc<AbsolutePath>,
},
Copied {
source_path: SourcePath,
destination_path: Arc<AbsolutePath>,
},
Error {
operation: &'static str,
error: FsError,
source_path: SourcePath,
destination_path: Arc<AbsolutePath>,
},
}
pub fn execute_commands(
commands: impl IntoIterator<Item = Command>,
dry_run: bool,
mut log: impl FnMut(&AbsolutePath, ExecutionLog),
) {
for command in commands {
let mut command_log = |entry| log(&command.defined_in, entry);
let (operation, source_path, destination_path, result) = match command.kind {
CommandKind::Link {
source_path,
destination_path,
} => {
let result =
execute_link(&source_path, &destination_path, dry_run, &mut command_log);
("link", source_path, destination_path, result)
}
CommandKind::Copy {
source_path,
destination_path,
} => {
let result =
execute_copy(&source_path, &destination_path, dry_run, &mut command_log);
("copy", source_path, destination_path, result)
}
};
if let Err(error) = result {
command_log(ExecutionLog::Error {
operation,
error,
source_path,
destination_path,
});
}
}
}
fn execute_link(
source_path: &SourcePath,
destination_path: &AbsolutePath,
dry_run: bool,
log: &mut impl FnMut(ExecutionLog),
) -> Result<(), FsError> {
create_parent_dir(destination_path, dry_run, log)?;
let replacing_symlink = if let Some(metadata) =
abs_fs::symlink_metadata_if_exists(destination_path)?
&& metadata.is_symlink()
{
if !dry_run {
abs_fs::remove_path(destination_path, metadata.file_type())?;
}
true
} else {
false
};
let create_result = source_path
.metadata()
.map_err(|error| FsError::Io(source_path.to_path_buf(), error))
.and_then(|metadata| {
if dry_run {
Ok(())
} else {
abs_fs::create_symlink(source_path, destination_path, metadata.file_type())
}
});
if let Err(error) = create_result {
if !dry_run && replacing_symlink {
log(ExecutionLog::PathDeleted(destination_path.into()));
}
return Err(error);
}
log(if replacing_symlink {
ExecutionLog::SymlinkRecreated {
source_path: source_path.clone(),
destination_path: destination_path.into(),
}
} else {
ExecutionLog::SymlinkCreated {
source_path: source_path.clone(),
destination_path: destination_path.into(),
}
});
Ok(())
}
fn execute_copy(
source_path: &SourcePath,
destination_path: &AbsolutePath,
dry_run: bool,
log: &mut impl FnMut(ExecutionLog),
) -> Result<(), FsError> {
create_parent_dir(destination_path, dry_run, log)?;
if !dry_run {
abs_fs::copy_overlay(source_path, destination_path)?;
}
log(ExecutionLog::Copied {
source_path: source_path.clone(),
destination_path: destination_path.into(),
});
Ok(())
}
fn create_parent_dir(
destination_path: &AbsolutePath,
dry_run: bool,
log: &mut impl FnMut(ExecutionLog),
) -> Result<(), FsError> {
let parent = if dry_run {
destination_path.parent().filter(|parent| !parent.is_dir())
} else {
abs_fs::create_parent_dir(destination_path)?
};
if let Some(parent) = parent {
log(ExecutionLog::DirectoryCreated(parent.into()));
}
Ok(())
}