From b6efa0a8188e91d525e7c9e2bf3be90ef7510da7 Mon Sep 17 00:00:00 2001 From: Niklas Kapelle Date: Wed, 4 Mar 2026 19:56:33 +0100 Subject: [PATCH] made use of walkdir --- Cargo.lock | 29 +++++++++++++++++++++++ Cargo.toml | 1 + src/basic_types.rs | 43 ++++++++++++++++++++++------------ src/instance.rs | 57 +++++++++++++++++++++++++++++++++------------- src/load_order.rs | 35 +++++++++++++++++++--------- src/utils.rs | 25 +------------------- 6 files changed, 124 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f45f2b..3e4f014 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -325,6 +325,7 @@ dependencies = [ "serde", "thiserror", "toml", + "walkdir", ] [[package]] @@ -725,6 +726,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "saphyr" version = "0.0.6" @@ -896,12 +906,31 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + [[package]] name = "windows-link" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index c6cf405..c389a57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ quick-xml = { version = "0.39.2", features = ["serde-types", "serialize"] } serde = { version = "1.0.228", features = ["derive"] } thiserror = "2.0.18" toml = "1.0.3" +walkdir = "2.5.0" diff --git a/src/basic_types.rs b/src/basic_types.rs index b337d59..41e9f27 100644 --- a/src/basic_types.rs +++ b/src/basic_types.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use log::trace; use serde::{Deserialize, Serialize}; use std::{ @@ -6,11 +7,9 @@ use std::{ path::{Path, PathBuf}, }; use thiserror::Error; +use walkdir::WalkDir; -use crate::{ - fomod::{FileType, FileTypeEnum}, - utils::walk_files_recursive, -}; +use crate::fomod::{FileType, FileTypeEnum}; /// A link between a file from a mod and a destination in a ModdedInstance #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] @@ -113,11 +112,19 @@ pub struct Game { impl Game { pub fn export_links(&self) -> Result, io::Error> { - let links: Vec = walk_files_recursive(&self.install_location) - .unwrap() - .map(|file| file.path()) - .map(|path| Link::new(&path, path.strip_prefix(&self.install_location).unwrap())) - .collect(); + let links: Vec = WalkDir::new(&self.install_location) + .into_iter() + .map(|entry| { + let entry = entry?; + let path = entry.path(); + + Ok(Link::new( + &path, + path.strip_prefix(&self.install_location).unwrap(), + )) + }) + .collect::>()?; + Ok(links) } } @@ -317,13 +324,19 @@ impl ModFile { let dest_base: PathBuf = Path::new("Data").join(PathBuf::from(dir_type.destination.unwrap_or_default())); - Ok(walk_files_recursive(&source_root)? - .map(|file| Self { - internal_priority: priority, - source: file.path().strip_prefix(&source).unwrap().to_owned(), - dest: dest_base.join(file.path().strip_prefix(&source_root).unwrap()), + let files = WalkDir::new(&source_root) + .into_iter() + .map(|entry| { + let entry = entry?; + Ok(Self { + internal_priority: priority, + source: entry.path().strip_prefix(&source).unwrap().to_owned(), + dest: dest_base.join(entry.path().strip_prefix(&source_root).unwrap()), + }) }) - .collect()) + .collect::>()?; + + Ok(files) } } } diff --git a/src/instance.rs b/src/instance.rs index 339175c..88a6950 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1,18 +1,19 @@ use std::{ - collections::{HashMap, HashSet}, + collections::HashMap, io, path::{Path, PathBuf}, }; use globset::{Glob, GlobSet, GlobSetBuilder}; use log::warn; +use walkdir::WalkDir; use crate::{ - basic_types::{InstalledMod, Link, ModConfig, ModFile, ModdedInstance, RootConfig}, + basic_types::{InstalledMod, ModConfig, ModFile, ModdedInstance, RootConfig}, file_conflict_solver::ConflictSolver, fomod, install_prompt, mod_config_installer::run_fomod_installer, - utils::{resolve_case_insensitive, walk_files_recursive}, + utils::resolve_case_insensitive, }; pub fn insert_mod_to_instance( @@ -129,12 +130,23 @@ fn install_from_dir( path: impl AsRef, ) -> anyhow::Result> { let glob_filter = create_glob_filter(mod_config.ignore())?; - let files: Vec<_> = walk_files_recursive(&path)? - .map(|entry| entry.path()) - .map(|file_path| file_path.strip_prefix(&path).unwrap().to_owned()) - .filter(|rel_path| !glob_filter.is_match(rel_path)) - .map(|rel_path| ModFile::new(&rel_path, &rel_path, 0)) - .collect(); + let files: Vec<_> = WalkDir::new(path) + .into_iter() + .map(|entry| { + let entry = entry?; + let path = entry.path(); + + let rel_path = path.strip_prefix(&path).unwrap(); + + if !glob_filter.is_match(rel_path) { + Ok(Some(ModFile::new(&rel_path, &rel_path, 0))) + } else { + Ok(None) + } + }) + .filter_map(|r| r.transpose()) + .collect::>()?; + Ok(files) } @@ -144,13 +156,26 @@ fn install_from_dir_to_data( ) -> anyhow::Result> { let glob_filter = create_glob_filter(mod_config.ignore())?; let data = PathBuf::from("Data"); - let files: Vec<_> = walk_files_recursive(&path)? - .map(|entry| entry.path()) - .map(|file_path| file_path.strip_prefix(&path).unwrap().to_owned()) - .filter(|rel_path| should_be_included(rel_path)) - .filter(|rel_path| !glob_filter.is_match(rel_path)) - .map(|rel_path| ModFile::new(&rel_path, data.join(&rel_path), 0)) - .collect(); + let files: Vec<_> = WalkDir::new(&path) + .into_iter() + .map(|entry| { + let entry = entry?; + let path = entry.path(); + + let rel_path = path.strip_prefix(&path).unwrap(); + if !should_be_included(rel_path) { + return Ok(None); + } + + if glob_filter.is_match(rel_path) { + return Ok(None); + } + + Ok(Some(ModFile::new(rel_path, data.join(rel_path), 0))) + }) + .filter_map(|r| r.transpose()) + .collect::>()?; + Ok(files) } diff --git a/src/load_order.rs b/src/load_order.rs index a11c23e..aa3cb94 100644 --- a/src/load_order.rs +++ b/src/load_order.rs @@ -3,13 +3,14 @@ use libloot::{ error::{GameHandleCreationError, LoadPluginsError, SortPluginsError}, }; use log::trace; -use std::{io, path::Path}; -use thiserror::Error; - -use crate::{ - basic_types::{self, ModdedInstance, RootConfig}, - utils::walk_files_recursive, +use std::{ + io, + path::{Path, PathBuf}, }; +use thiserror::Error; +use walkdir::WalkDir; + +use crate::basic_types::{self, ModdedInstance, RootConfig}; pub fn create_loadorder( root_config: &RootConfig, @@ -19,11 +20,23 @@ pub fn create_loadorder( let mut loot_game = Game::new(GameType::SkyrimSE, &game.install_location)?; // Add plugins files from the game install - let install_plugins: Vec<_> = walk_files_recursive(game.install_location.join("Data"))? - .filter(|f| is_plugin_file(f.path())) - .map(|f| f.path()) - .collect(); - let refs: Vec<_> = install_plugins.iter().map(|e| e.as_path()).collect(); + let install_plugins: Vec = WalkDir::new(game.install_location.join("Data")) + .into_iter() + .map(|entry| { + let entry = entry?; + let path = entry.path(); + + if is_plugin_file(path) { + Ok(Some(path.to_path_buf())) + } else { + Ok(None) + } + }) + .filter_map(|r| r.transpose()) + .collect::>()?; + + // The loaded_plugins function requires &[&Path] + let refs: Vec<&Path> = install_plugins.iter().map(|e| e.as_path()).collect(); trace!("Loading {} plugins to game", refs.len()); loot_game.load_plugins(&refs)?; diff --git a/src/utils.rs b/src/utils.rs index f007b37..6874537 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,32 +1,9 @@ use std::{ - fs::{self, DirEntry}, + fs::{self}, io, path::{Path, PathBuf}, }; -pub fn walk_files_recursive( - root: impl AsRef, -) -> std::io::Result> { - fn visit(dir: &Path, out: &mut Vec) -> std::io::Result<()> { - for entry in fs::read_dir(dir)? { - let entry = entry?; - let path = entry.path(); - let file_type = entry.file_type()?; - - if file_type.is_dir() { - visit(&path, out)?; - } else if file_type.is_file() { - out.push(entry); - } - } - Ok(()) - } - - let mut files = Vec::new(); - visit(root.as_ref(), &mut files)?; - Ok(files.into_iter()) -} - pub fn path_to_lowercase(path: impl AsRef) -> PathBuf { PathBuf::from(path.as_ref().to_string_lossy().to_lowercase()) }