mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Decouple PluginSortingData and Plugin
The SortingPlugin trait will make testing the sorting code easier, as it won't need to load plugin files.
This commit is contained in:
+83
-30
@@ -25,8 +25,8 @@ use super::{
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PluginSortingData<'a> {
|
||||
plugin: &'a Plugin,
|
||||
pub struct PluginSortingData<'a, T: SortingPlugin> {
|
||||
plugin: &'a T,
|
||||
pub(super) is_master: bool,
|
||||
override_record_count: usize,
|
||||
|
||||
@@ -40,9 +40,9 @@ pub struct PluginSortingData<'a> {
|
||||
pub(super) user_req: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'a> PluginSortingData<'a> {
|
||||
impl<'a, T: SortingPlugin> PluginSortingData<'a, T> {
|
||||
pub fn new(
|
||||
plugin: &'a Plugin,
|
||||
plugin: &'a T,
|
||||
masterlist_metadata: Option<&PluginMetadata>,
|
||||
user_metadata: Option<&PluginMetadata>,
|
||||
load_order_index: usize,
|
||||
@@ -91,15 +91,56 @@ impl<'a> PluginSortingData<'a> {
|
||||
self.plugin.masters()
|
||||
}
|
||||
|
||||
fn do_records_overlap(&self, other: &PluginSortingData) -> Result<bool, PluginDataError> {
|
||||
fn do_records_overlap(&self, other: &Self) -> Result<bool, PluginDataError> {
|
||||
self.plugin.do_records_overlap(other.plugin)
|
||||
}
|
||||
|
||||
fn do_assets_overlap(&self, other: &PluginSortingData) -> bool {
|
||||
fn do_assets_overlap(&self, other: &Self) -> bool {
|
||||
self.plugin.do_assets_overlap(other.plugin)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SortingPlugin {
|
||||
fn name(&self) -> &str;
|
||||
fn is_master(&self) -> bool;
|
||||
fn is_blueprint_plugin(&self) -> bool;
|
||||
fn masters(&self) -> Result<Vec<String>, PluginDataError>;
|
||||
fn override_record_count(&self) -> Result<usize, PluginDataError>;
|
||||
fn asset_count(&self) -> usize;
|
||||
fn do_records_overlap(&self, other: &Self) -> Result<bool, PluginDataError>;
|
||||
fn do_assets_overlap(&self, other: &Self) -> bool;
|
||||
}
|
||||
|
||||
impl SortingPlugin for Plugin {
|
||||
fn name(&self) -> &str {
|
||||
self.name()
|
||||
}
|
||||
fn is_master(&self) -> bool {
|
||||
self.is_master()
|
||||
}
|
||||
|
||||
fn is_blueprint_plugin(&self) -> bool {
|
||||
self.is_blueprint_plugin()
|
||||
}
|
||||
|
||||
fn masters(&self) -> Result<Vec<String>, PluginDataError> {
|
||||
self.masters()
|
||||
}
|
||||
fn override_record_count(&self) -> Result<usize, PluginDataError> {
|
||||
self.override_record_count()
|
||||
}
|
||||
fn asset_count(&self) -> usize {
|
||||
self.asset_count()
|
||||
}
|
||||
|
||||
fn do_records_overlap(&self, other: &Self) -> Result<bool, PluginDataError> {
|
||||
self.do_records_overlap(other)
|
||||
}
|
||||
fn do_assets_overlap(&self, other: &Self) -> bool {
|
||||
self.do_assets_overlap(other)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_filenames(files: &[File]) -> Vec<String> {
|
||||
files
|
||||
.iter()
|
||||
@@ -107,21 +148,21 @@ fn to_filenames(files: &[File]) -> Vec<String> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
type InnerPluginsGraph<'a> = Graph<Rc<PluginSortingData<'a>>, EdgeType>;
|
||||
type InnerPluginsGraph<'a, T> = Graph<Rc<PluginSortingData<'a, T>>, EdgeType>;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PluginsGraph<'a> {
|
||||
#[derive(Debug)]
|
||||
struct PluginsGraph<'a, T: SortingPlugin> {
|
||||
// Put the sorting data in Rc so that it can be held onto while mutating the graph.
|
||||
inner: InnerPluginsGraph<'a>,
|
||||
inner: InnerPluginsGraph<'a, T>,
|
||||
paths_cache: HashMap<NodeIndex, HashSet<NodeIndex>>,
|
||||
}
|
||||
|
||||
impl<'a> PluginsGraph<'a> {
|
||||
impl<'a, T: SortingPlugin> PluginsGraph<'a, T> {
|
||||
fn new() -> Self {
|
||||
PluginsGraph::default()
|
||||
}
|
||||
|
||||
fn add_node(&mut self, plugin: PluginSortingData<'a>) {
|
||||
fn add_node(&mut self, plugin: PluginSortingData<'a, T>) {
|
||||
self.inner.add_node(Rc::new(plugin));
|
||||
}
|
||||
|
||||
@@ -711,16 +752,26 @@ impl<'a> PluginsGraph<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::Index<NodeIndex> for PluginsGraph<'a> {
|
||||
type Output = Rc<PluginSortingData<'a>>;
|
||||
// The derive macro for Default requires T: Default, but it's not actually necessary.
|
||||
impl<T: SortingPlugin> std::default::Default for PluginsGraph<'_, T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: Default::default(),
|
||||
paths_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: SortingPlugin> std::ops::Index<NodeIndex> for PluginsGraph<'a, T> {
|
||||
type Output = Rc<PluginSortingData<'a, T>>;
|
||||
|
||||
fn index(&self, index: NodeIndex) -> &Self::Output {
|
||||
&self.inner[index]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort_plugins(
|
||||
mut plugins_sorting_data: Vec<PluginSortingData>,
|
||||
pub fn sort_plugins<T: SortingPlugin>(
|
||||
mut plugins_sorting_data: Vec<PluginSortingData<T>>,
|
||||
groups_graph: &GroupsGraph,
|
||||
early_loading_plugins: &[String],
|
||||
) -> Result<Vec<String>, SortingError> {
|
||||
@@ -778,8 +829,8 @@ pub fn sort_plugins(
|
||||
Ok(masters_load_order)
|
||||
}
|
||||
|
||||
fn sort_plugins_partition(
|
||||
plugins_sorting_data: Vec<PluginSortingData>,
|
||||
fn sort_plugins_partition<T: SortingPlugin>(
|
||||
plugins_sorting_data: Vec<PluginSortingData<T>>,
|
||||
groups_graph: &GroupsGraph,
|
||||
early_loading_plugins: &[String],
|
||||
) -> Result<Vec<String>, SortingError> {
|
||||
@@ -824,7 +875,7 @@ fn sort_plugins_partition(
|
||||
Ok(sorted_plugin_names)
|
||||
}
|
||||
|
||||
fn path_to_string(graph: &InnerPluginsGraph, path: &[NodeIndex]) -> String {
|
||||
fn path_to_string<T: SortingPlugin>(graph: &InnerPluginsGraph<T>, path: &[NodeIndex]) -> String {
|
||||
path.iter()
|
||||
.map(|i| graph[*i].name())
|
||||
.collect::<Vec<_>>()
|
||||
@@ -832,8 +883,8 @@ fn path_to_string(graph: &InnerPluginsGraph, path: &[NodeIndex]) -> String {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PathFinder<'a, 'b> {
|
||||
graph: &'a InnerPluginsGraph<'b>,
|
||||
struct PathFinder<'a, 'b, T: SortingPlugin> {
|
||||
graph: &'a InnerPluginsGraph<'b, T>,
|
||||
cache: &'a mut HashMap<NodeIndex, HashSet<NodeIndex>>,
|
||||
from_node_index: NodeIndex,
|
||||
to_node_index: NodeIndex,
|
||||
@@ -842,9 +893,9 @@ struct PathFinder<'a, 'b> {
|
||||
intersection_node: Option<NodeIndex>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> PathFinder<'a, 'b> {
|
||||
impl<'a, 'b, T: SortingPlugin> PathFinder<'a, 'b, T> {
|
||||
fn new(
|
||||
graph: &'a InnerPluginsGraph<'b>,
|
||||
graph: &'a InnerPluginsGraph<'b, T>,
|
||||
cache: &'a mut HashMap<NodeIndex, HashSet<NodeIndex>>,
|
||||
from_node_index: NodeIndex,
|
||||
to_node_index: NodeIndex,
|
||||
@@ -914,7 +965,7 @@ impl<'a, 'b> PathFinder<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
impl BidirBfsVisitor for PathFinder<'_, '_> {
|
||||
impl<T: SortingPlugin> BidirBfsVisitor for PathFinder<'_, '_, T> {
|
||||
fn visit_forward_bfs_edge(&mut self, source: NodeIndex, target: NodeIndex) {
|
||||
self.cache_path(self.from_node_index, target);
|
||||
|
||||
@@ -939,7 +990,9 @@ struct PathCacher<'a> {
|
||||
to_node_index: NodeIndex,
|
||||
}
|
||||
|
||||
fn get_plugins_in_groups(graph: &InnerPluginsGraph) -> HashMap<String, Vec<NodeIndex>> {
|
||||
fn get_plugins_in_groups<T: SortingPlugin>(
|
||||
graph: &InnerPluginsGraph<T>,
|
||||
) -> HashMap<String, Vec<NodeIndex>> {
|
||||
let mut plugins_in_groups: HashMap<String, Vec<NodeIndex>> = HashMap::new();
|
||||
|
||||
for node in graph.node_indices() {
|
||||
@@ -985,8 +1038,8 @@ impl BidirBfsVisitor for PathCacher<'_> {
|
||||
type PluginNodeIndex = NodeIndex;
|
||||
type GroupNodeIndex = NodeIndex;
|
||||
|
||||
struct GroupsPathVisitor<'a, 'b, 'c, 'd, 'e> {
|
||||
plugins_graph: &'a mut PluginsGraph<'b>,
|
||||
struct GroupsPathVisitor<'a, 'b, 'c, 'd, 'e, T: SortingPlugin> {
|
||||
plugins_graph: &'a mut PluginsGraph<'b, T>,
|
||||
groups_graph: &'e GroupsGraph,
|
||||
groups_plugins: &'c HashMap<String, Vec<PluginNodeIndex>>,
|
||||
finished_group_vertices: &'d mut HashSet<GroupNodeIndex>,
|
||||
@@ -995,9 +1048,9 @@ struct GroupsPathVisitor<'a, 'b, 'c, 'd, 'e> {
|
||||
unfinishable_nodes: HashSet<GroupNodeIndex>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, 'd, 'e> GroupsPathVisitor<'a, 'b, 'c, 'd, 'e> {
|
||||
impl<'a, 'b, 'c, 'd, 'e, T: SortingPlugin> GroupsPathVisitor<'a, 'b, 'c, 'd, 'e, T> {
|
||||
fn new(
|
||||
plugins_graph: &'a mut PluginsGraph<'b>,
|
||||
plugins_graph: &'a mut PluginsGraph<'b, T>,
|
||||
groups_graph: &'e GroupsGraph,
|
||||
groups_plugins: &'c HashMap<String, Vec<PluginNodeIndex>>,
|
||||
finished_group_vertices: &'d mut HashSet<GroupNodeIndex>,
|
||||
@@ -1079,7 +1132,7 @@ impl<'a, 'b, 'c, 'd, 'e> GroupsPathVisitor<'a, 'b, 'c, 'd, 'e> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'e> DfsVisitor<'e> for GroupsPathVisitor<'_, '_, '_, '_, 'e> {
|
||||
impl<'e, T: SortingPlugin> DfsVisitor<'e> for GroupsPathVisitor<'_, '_, '_, '_, 'e, T> {
|
||||
fn visit_tree_edge(&mut self, edge_ref: EdgeReference<'e, EdgeType>) {
|
||||
let source = edge_ref.source();
|
||||
let target = edge_ref.target();
|
||||
|
||||
+18
-15
@@ -7,10 +7,13 @@ use crate::{
|
||||
sorting::error::{CyclicInteractionError, PluginGraphValidationError, UndefinedGroupError},
|
||||
};
|
||||
|
||||
use super::{groups::GroupsGraph, plugins::PluginSortingData};
|
||||
use super::{
|
||||
groups::GroupsGraph,
|
||||
plugins::{PluginSortingData, SortingPlugin},
|
||||
};
|
||||
|
||||
pub fn validate_plugin_groups(
|
||||
plugins_sorting_data: &[PluginSortingData<'_>],
|
||||
pub fn validate_plugin_groups<T: SortingPlugin>(
|
||||
plugins_sorting_data: &[PluginSortingData<'_, T>],
|
||||
groups_graph: &GroupsGraph,
|
||||
) -> Result<(), UndefinedGroupError> {
|
||||
let group_names: HashSet<&String> = groups_graph
|
||||
@@ -27,10 +30,10 @@ pub fn validate_plugin_groups(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_specific_and_hardcoded_edges(
|
||||
masters: &[PluginSortingData<'_>],
|
||||
blueprint_masters: &[PluginSortingData<'_>],
|
||||
non_masters: &[PluginSortingData<'_>],
|
||||
pub fn validate_specific_and_hardcoded_edges<T: SortingPlugin>(
|
||||
masters: &[PluginSortingData<'_, T>],
|
||||
blueprint_masters: &[PluginSortingData<'_, T>],
|
||||
non_masters: &[PluginSortingData<'_, T>],
|
||||
early_loading_plugins: &[String],
|
||||
) -> Result<(), PluginGraphValidationError> {
|
||||
log::trace!("Validating specific and early-loading plugin edges...");
|
||||
@@ -53,8 +56,8 @@ pub fn validate_specific_and_hardcoded_edges(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_masters(
|
||||
masters: &[PluginSortingData<'_>],
|
||||
fn validate_masters<T: SortingPlugin>(
|
||||
masters: &[PluginSortingData<'_, T>],
|
||||
non_masters: &HashSet<UniCase<&str>>,
|
||||
blueprint_masters: &HashSet<UniCase<&str>>,
|
||||
) -> Result<(), PluginGraphValidationError> {
|
||||
@@ -66,8 +69,8 @@ fn validate_masters(
|
||||
.try_for_each(|m| validate_plugin(m, non_masters, blueprint_masters))
|
||||
}
|
||||
|
||||
fn validate_non_masters(
|
||||
non_masters: &[PluginSortingData<'_>],
|
||||
fn validate_non_masters<T: SortingPlugin>(
|
||||
non_masters: &[PluginSortingData<'_, T>],
|
||||
blueprint_masters: &HashSet<UniCase<&str>>,
|
||||
) -> Result<(), PluginGraphValidationError> {
|
||||
log::trace!("Validating specific and early-loading plugin edges for non-master files...");
|
||||
@@ -80,8 +83,8 @@ fn validate_non_masters(
|
||||
.try_for_each(|p| validate_plugin(p, &empty_set, blueprint_masters))
|
||||
}
|
||||
|
||||
fn validate_plugin(
|
||||
plugin: &PluginSortingData<'_>,
|
||||
fn validate_plugin<T: SortingPlugin>(
|
||||
plugin: &PluginSortingData<'_, T>,
|
||||
non_masters: &HashSet<UniCase<&str>>,
|
||||
blueprint_masters: &HashSet<UniCase<&str>>,
|
||||
) -> Result<(), PluginGraphValidationError> {
|
||||
@@ -176,9 +179,9 @@ fn validate_files(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_early_loading_plugins(
|
||||
fn validate_early_loading_plugins<T: SortingPlugin>(
|
||||
early_loading_plugins: &[String],
|
||||
masters: &[PluginSortingData<'_>],
|
||||
masters: &[PluginSortingData<'_, T>],
|
||||
non_masters: &HashSet<UniCase<&str>>,
|
||||
) -> Result<(), CyclicInteractionError> {
|
||||
if let Some(master) = masters.first() {
|
||||
|
||||
Reference in New Issue
Block a user