Skip to content

Commit

Permalink
Implement TextfileBasedLoadOrder::load()
Browse files Browse the repository at this point in the history
Refactor parsing of active plugins file and load order file to be
generic, taking a mapper function to apply game-specific logic.
  • Loading branch information
Ortham committed Aug 25, 2017
1 parent 5846c47 commit 0dcfd4d
Show file tree
Hide file tree
Showing 6 changed files with 399 additions and 72 deletions.
8 changes: 8 additions & 0 deletions src/load_order/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

use std::borrow::Cow;
use std::io;
use std::string::FromUtf8Error;
use regex;
use error::Error;

Expand All @@ -35,6 +36,7 @@ pub enum LoadOrderError {
DuplicatePlugin,
NonMasterBeforeMaster,
GameMasterMustLoadFirst,
NotUtf8(Vec<u8>),
}

impl From<Error> for LoadOrderError {
Expand All @@ -60,3 +62,9 @@ impl From<regex::Error> for LoadOrderError {
LoadOrderError::InvalidRegex
}
}

impl From<FromUtf8Error> for LoadOrderError {
fn from(error: FromUtf8Error) -> Self {
LoadOrderError::NotUtf8(error.into_bytes())
}
}
36 changes: 36 additions & 0 deletions src/load_order/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ mod tests;
mod textfile_based;
mod timestamp_based;

use std::fs::File;
use std::io::{BufReader, BufRead};
use std::path::Path;

use unicase::eq;

use plugin::Plugin;
use load_order::error::LoadOrderError;

fn match_plugin(plugin: &Plugin, name: &str) -> bool {
match plugin.name() {
Expand All @@ -39,3 +44,34 @@ fn match_plugin(plugin: &Plugin, name: &str) -> bool {
fn find_first_non_master_position(plugins: &[Plugin]) -> Option<usize> {
plugins.iter().position(|p| !p.is_master_file())
}

fn trim_cr(mut buffer: Vec<u8>) -> Vec<u8> {
if buffer.last() == Some(&b'\r') {
buffer.pop();
}
buffer
}

fn read_plugin_names<F>(file_path: &Path, line_mapper: F) -> Result<Vec<String>, LoadOrderError>
where
F: Fn(Vec<u8>) -> Result<String, LoadOrderError>,
{
if !file_path.exists() {
return Ok(Vec::new());
}

let input = File::open(file_path)?;
let buffered = BufReader::new(input);

let mut names: Vec<String> = Vec::new();
for line in buffered.split(b'\n') {
let line = line_mapper(trim_cr(line?))?;
if line.is_empty() || line.starts_with('#') {
continue;
}

names.push(line);
}

Ok(names)
}
63 changes: 52 additions & 11 deletions src/load_order/mutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use std::mem;
use walkdir::WalkDir;

use game_settings::GameSettings;
use load_order::{find_first_non_master_position, match_plugin};
use load_order::{find_first_non_master_position, match_plugin, read_plugin_names};
use load_order::error::LoadOrderError;
use load_order::readable::ReadableLoadOrder;
use plugin::Plugin;
Expand Down Expand Up @@ -128,12 +128,30 @@ pub trait MutableLoadOrder: ReadableLoadOrder {
}
}

fn move_or_insert_plugin(
fn move_or_insert_plugin(&mut self, plugin_name: &str) -> Result<(), LoadOrderError> {
let plugin = get_plugin_to_insert(self, plugin_name, None)?;
let position = self.insert_position(&plugin);

match position {
None => self.mut_plugins().push(plugin),
Some(x) => self.mut_plugins().insert(x, plugin),
}

Ok(())
}

fn move_or_insert_plugin_with_index(
&mut self,
plugin_name: &str,
position: usize,
) -> Result<(), LoadOrderError> {
let plugin = get_plugin_to_insert(self, plugin_name, position)?;
if let Some(x) = self.index_of(plugin_name) {
if x == position {
return Ok(());
}
}

let plugin = get_plugin_to_insert(self, plugin_name, Some(position))?;

if position >= self.plugins().len() {
self.mut_plugins().push(plugin);
Expand Down Expand Up @@ -183,6 +201,25 @@ pub trait MutableLoadOrder: ReadableLoadOrder {
}
}
}

fn load_active_plugins<F>(&mut self, line_mapper: F) -> Result<(), LoadOrderError>
where
F: Fn(Vec<u8>) -> Result<String, LoadOrderError>,
{
for plugin in self.mut_plugins() {
plugin.deactivate();
}

let plugin_names =
read_plugin_names(self.game_settings().active_plugins_file(), line_mapper)?;

for plugin_name in plugin_names {
let index = self.find_or_add(&plugin_name)?;
self.mut_plugins()[index].activate()?;
}

Ok(())
}
}

fn validate_index<T: MutableLoadOrder + ?Sized>(
Expand All @@ -204,24 +241,28 @@ fn validate_index<T: MutableLoadOrder + ?Sized>(
fn get_plugin_to_insert<T: MutableLoadOrder + ?Sized>(
load_order: &mut T,
plugin_name: &str,
position: usize,
insert_position: Option<usize>,
) -> Result<Plugin, LoadOrderError> {
if let Some(i) = load_order.plugins().iter().position(
let plugin_position = load_order.plugins().iter().position(
|p| match_plugin(p, plugin_name),
)
{
let is_master = load_order.plugins()[i].is_master_file();
validate_index(load_order, position, is_master)?;
);
if let Some(p) = plugin_position {
if let Some(i) = insert_position {
let is_master = load_order.plugins()[p].is_master_file();
validate_index(load_order, i, is_master)?;
}

Ok(load_order.mut_plugins().remove(i))
Ok(load_order.mut_plugins().remove(p))
} else {
if !Plugin::is_valid(plugin_name, load_order.game_settings()) {
return Err(LoadOrderError::InvalidPlugin(plugin_name.to_string()));
}

let plugin = Plugin::new(plugin_name, load_order.game_settings())?;

validate_index(load_order, position, plugin.is_master_file())?;
if let Some(i) = insert_position {
validate_index(load_order, i, plugin.is_master_file())?;
}

Ok(plugin)
}
Expand Down
13 changes: 10 additions & 3 deletions src/load_order/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* along with libespm. If not, see <http://www.gnu.org/licenses/>.
*/

use std::fs::File;
use std::io::Write;
use std::path::Path;
use encoding::{Encoding, EncoderTrap};
use encoding::all::WINDOWS_1252;
Expand All @@ -27,10 +29,15 @@ use game_settings::GameSettings;
use plugin::Plugin;
use tests::copy_to_test_dir;

pub fn write_active_plugins_file(game_settings: &GameSettings, filenames: &[&str]) {
use std::fs::File;
use std::io::Write;
pub fn write_load_order_file(game_settings: &GameSettings, filenames: &[&str]) {
let mut file = File::create(&game_settings.load_order_file().unwrap()).unwrap();

for filename in filenames {
writeln!(file, "{}", filename).unwrap();
}
}

pub fn write_active_plugins_file(game_settings: &GameSettings, filenames: &[&str]) {
let mut file = File::create(&game_settings.active_plugins_file()).unwrap();

if game_settings.id() == GameId::Morrowind {
Expand Down

0 comments on commit 0dcfd4d

Please sign in to comment.