diff --git a/src/linker.rs b/src/linker.rs index 263fbbd..cb0c215 100644 --- a/src/linker.rs +++ b/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, +) -> 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, + target: impl AsRef, +) -> 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 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) } }