refactored linker
This commit is contained in:
171
src/linker.rs
171
src/linker.rs
@@ -1,148 +1,61 @@
|
||||
use std::{
|
||||
fs, io,
|
||||
os::unix,
|
||||
path::{Path, PathBuf},
|
||||
use std::{fs, io, os::unix, path::Path};
|
||||
|
||||
use crate::{
|
||||
basic_types::{ModdedInstance, RootConfig},
|
||||
utils::walk_files_recursive,
|
||||
};
|
||||
|
||||
use crate::{Mod, ModFile, fomod::FileTypeEnum};
|
||||
pub fn link_instance_to_target(
|
||||
root_config: &RootConfig,
|
||||
instance: &ModdedInstance,
|
||||
target: impl AsRef<Path>,
|
||||
) -> Result<(), io::Error> {
|
||||
for installed_mod in &instance.mods {
|
||||
let mod_config = root_config.get_mod_by_id(&installed_mod.mod_id()).unwrap();
|
||||
let mod_source_root = root_config.get_mod_location(&mod_config);
|
||||
|
||||
pub struct Linker {
|
||||
target: PathBuf,
|
||||
game_dir: PathBuf,
|
||||
for (src, dst) in installed_mod.files() {
|
||||
let link_target = mod_source_root.join(src);
|
||||
let link_name = target.as_ref().join(dst);
|
||||
link_file(&link_target, &link_name)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Linker {
|
||||
pub fn new(target_path: &Path, game_dir: &Path) -> Self {
|
||||
Self {
|
||||
target: target_path.to_owned(),
|
||||
game_dir: game_dir.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn link_file(&self, from: &Path, to: &Path) -> Result<(), LinkerError> {
|
||||
let target = self.target.join(to);
|
||||
|
||||
if let Some(parent) = target.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
create_symlink_for_file(from, &target)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn link_recursive(&self, from: &Path, to: &Path) -> Result<(), LinkerError> {
|
||||
for entry in fs::read_dir(from)? {
|
||||
let entry = entry?;
|
||||
let entry_path = entry.path();
|
||||
let relative = entry_path
|
||||
.strip_prefix(from)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
|
||||
let target_path = to.join(relative);
|
||||
|
||||
if entry_path.is_dir() {
|
||||
self.link_recursive(&entry_path, &target_path)?;
|
||||
} else {
|
||||
self.link_file(&entry_path, &target_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn link_mod_file(&self, file: &ModFile, from_mod: &Mod) -> Result<(), LinkerError> {
|
||||
let src = from_mod.source.join(&file.source);
|
||||
|
||||
self.link_file(&src, &file.dest)
|
||||
}
|
||||
|
||||
pub fn link_plugin_files(
|
||||
&self,
|
||||
entries: &[FileTypeEnum],
|
||||
mod_dir: &Path,
|
||||
) -> Result<(), LinkerError> {
|
||||
let mut sorted_entries = entries.to_owned();
|
||||
sorted_entries.sort_by_cached_key(|e| match e {
|
||||
FileTypeEnum::File(file_type) => file_type.priority.unwrap_or(0),
|
||||
FileTypeEnum::Folder(file_type) => file_type.priority.unwrap_or(0),
|
||||
});
|
||||
|
||||
for entry in sorted_entries {
|
||||
match entry {
|
||||
FileTypeEnum::File(file) => {
|
||||
let from = mod_dir.join(file.source);
|
||||
let to = Path::new("Data").join(file.destination.unwrap_or("".to_owned()));
|
||||
self.link_file(&from, &to)?;
|
||||
}
|
||||
FileTypeEnum::Folder(folder) => {
|
||||
let from = mod_dir.join(folder.source);
|
||||
let to = Path::new("Data").join(folder.destination.unwrap_or("".to_owned()));
|
||||
self.link_recursive(&from, &to)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn link_install_to_target(&self) -> Result<(), LinkerError> {
|
||||
fn symlink_tree(src: &Path, dst: &Path) -> Result<(), LinkerError> {
|
||||
if !dst.exists() {
|
||||
fs::create_dir_all(dst)?;
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let src_path = entry.path();
|
||||
let dst_path = dst.join(entry.path().file_name().unwrap());
|
||||
|
||||
let meta = entry.metadata()?;
|
||||
if meta.is_dir() {
|
||||
symlink_tree(&src_path, &dst_path)?;
|
||||
} else if meta.is_file() {
|
||||
create_symlink_for_file(&src_path, &dst_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
symlink_tree(&self.game_dir, &self.target)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn link_game_to_target(
|
||||
game_dir: impl AsRef<Path>,
|
||||
target: impl AsRef<Path>,
|
||||
) -> Result<(), io::Error> {
|
||||
walk_files_recursive(&game_dir)?.try_for_each(|file| {
|
||||
let link_target = file.path();
|
||||
let link_name = target
|
||||
.as_ref()
|
||||
.join(file.path().strip_prefix(&game_dir).unwrap());
|
||||
link_file(&link_target, &link_name)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LinkerError {
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LinkerError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Io(e) => write!(f, "IO error: {}", e),
|
||||
}
|
||||
fn link_file(target: &Path, link_name: &Path) -> Result<(), io::Error> {
|
||||
if let Some(parent) = link_name.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
create_symlink_for_file(target, link_name)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl std::error::Error for LinkerError {}
|
||||
|
||||
impl From<io::Error> for LinkerError {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Self::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_symlink_for_file(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
let absolute_path = fs::canonicalize(src)?;
|
||||
fn create_symlink_for_file(target: &Path, link_name: &Path) -> io::Result<()> {
|
||||
let absolute_path = fs::canonicalize(target)?;
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
unix::fs::symlink(absolute_path, dst)
|
||||
unix::fs::symlink(absolute_path, link_name)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
std::os::windows::fs::symlink_file(src, dst)
|
||||
std::os::windows::fs::symlink_file(target, link_name)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user