loadorder/load_order/
readable.rs1use crate::game_settings::GameSettings;
20use crate::plugin::Plugin;
21
22pub trait ReadableLoadOrderBase {
23 fn plugins(&self) -> &[Plugin];
24
25 fn game_settings_base(&self) -> &GameSettings;
26
27 fn find_plugin(&self, plugin_name: &str) -> Option<&Plugin> {
28 self.plugins().iter().find(|p| p.name_matches(plugin_name))
29 }
30
31 fn find_plugin_and_index(&self, plugin_name: &str) -> Option<(usize, &Plugin)> {
32 self.plugins()
33 .iter()
34 .enumerate()
35 .find(|(_, p)| p.name_matches(plugin_name))
36 }
37}
38
39pub trait ReadableLoadOrder {
40 fn game_settings(&self) -> &GameSettings;
41
42 fn plugin_names(&self) -> Vec<&str>;
43
44 fn index_of(&self, plugin_name: &str) -> Option<usize>;
45
46 fn plugin_at(&self, index: usize) -> Option<&str>;
47
48 fn active_plugin_names(&self) -> Vec<&str>;
49
50 fn is_active(&self, plugin_name: &str) -> bool;
51}
52
53impl<T: ReadableLoadOrderBase> ReadableLoadOrder for T {
54 fn game_settings(&self) -> &GameSettings {
55 self.game_settings_base()
56 }
57
58 fn plugin_names(&self) -> Vec<&str> {
59 self.plugins().iter().map(Plugin::name).collect()
60 }
61
62 fn index_of(&self, plugin_name: &str) -> Option<usize> {
63 self.plugins()
64 .iter()
65 .position(|p| p.name_matches(plugin_name))
66 }
67
68 fn plugin_at(&self, index: usize) -> Option<&str> {
69 self.plugins().get(index).map(Plugin::name)
70 }
71
72 fn active_plugin_names(&self) -> Vec<&str> {
73 self.plugins()
74 .iter()
75 .filter(|p| p.is_active())
76 .map(Plugin::name)
77 .collect()
78 }
79
80 fn is_active(&self, plugin_name: &str) -> bool {
81 self.find_plugin(plugin_name).is_some_and(Plugin::is_active)
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 use std::path::Path;
90
91 use tempfile::tempdir;
92
93 use crate::enums::GameId;
94 use crate::load_order::tests::{game_settings_for_test, mock_game_files};
95 use crate::tests::copy_to_test_dir;
96
97 struct TestLoadOrder {
98 game_settings: GameSettings,
99 plugins: Vec<Plugin>,
100 }
101
102 impl ReadableLoadOrderBase for TestLoadOrder {
103 fn game_settings_base(&self) -> &GameSettings {
104 &self.game_settings
105 }
106
107 fn plugins(&self) -> &[Plugin] {
108 &self.plugins
109 }
110 }
111
112 fn prepare(game_dir: &Path) -> TestLoadOrder {
113 let mut game_settings = game_settings_for_test(GameId::Oblivion, game_dir);
114 mock_game_files(&mut game_settings);
115
116 let plugins = vec![
117 Plugin::with_active("Blank.esp", &game_settings, true).unwrap(),
118 Plugin::new("Blank - Different.esp", &game_settings).unwrap(),
119 ];
120
121 TestLoadOrder {
122 game_settings,
123 plugins,
124 }
125 }
126
127 fn prepare_with_ghosted_plugin(game_dir: &Path) -> TestLoadOrder {
128 let mut load_order = prepare(game_dir);
129
130 copy_to_test_dir(
131 "Blank - Different.esm",
132 "Blank - Different.esm.ghost",
133 &load_order.game_settings,
134 );
135 load_order.plugins.insert(
136 0,
137 Plugin::new("Blank - Different.esm.ghost", &load_order.game_settings).unwrap(),
138 );
139
140 load_order
141 }
142
143 #[test]
144 fn plugin_names_should_return_filenames_for_plugins_in_load_order() {
145 let tmp_dir = tempdir().unwrap();
146 let load_order = prepare(tmp_dir.path());
147
148 let expected_plugin_names = vec!["Blank.esp", "Blank - Different.esp"];
149 assert_eq!(expected_plugin_names, load_order.plugin_names());
150 }
151
152 #[test]
153 fn plugin_names_should_return_unghosted_filenames() {
154 let tmp_dir = tempdir().unwrap();
155 let load_order = prepare_with_ghosted_plugin(tmp_dir.path());
156
157 let expected_plugin_names = vec![
158 "Blank - Different.esm",
159 "Blank.esp",
160 "Blank - Different.esp",
161 ];
162 assert_eq!(expected_plugin_names, load_order.plugin_names());
163 }
164
165 #[test]
166 fn index_of_should_return_none_if_the_plugin_is_not_in_the_load_order() {
167 let tmp_dir = tempdir().unwrap();
168 let load_order = prepare(tmp_dir.path());
169
170 assert!(load_order.index_of("Blank.esm").is_none());
171 }
172
173 #[test]
174 fn index_of_should_return_some_index_if_the_plugin_is_in_the_load_order() {
175 let tmp_dir = tempdir().unwrap();
176 let load_order = prepare(tmp_dir.path());
177
178 assert_eq!(0, load_order.index_of("Blank.esp").unwrap());
179 }
180
181 #[test]
182 fn index_of_should_be_case_insensitive() {
183 let tmp_dir = tempdir().unwrap();
184 let load_order = prepare(tmp_dir.path());
185
186 assert_eq!(0, load_order.index_of("blank.esp").unwrap());
187 }
188
189 #[test]
190 fn plugin_at_should_return_none_if_given_an_out_of_bounds_index() {
191 let tmp_dir = tempdir().unwrap();
192 let load_order = prepare(tmp_dir.path());
193
194 assert!(load_order.plugin_at(3).is_none());
195 }
196
197 #[test]
198 fn plugin_at_should_return_some_filename_if_given_an_in_bounds_index() {
199 let tmp_dir = tempdir().unwrap();
200 let load_order = prepare(tmp_dir.path());
201
202 assert_eq!("Blank.esp", load_order.plugin_at(0).unwrap());
203 }
204
205 #[test]
206 fn plugin_at_should_return_some_unghosted_filename() {
207 let tmp_dir = tempdir().unwrap();
208 let load_order = prepare_with_ghosted_plugin(tmp_dir.path());
209
210 assert_eq!("Blank - Different.esm", load_order.plugin_at(0).unwrap());
211 }
212
213 #[test]
214 fn active_plugin_names_should_return_filenames_for_active_plugins_in_load_order() {
215 let tmp_dir = tempdir().unwrap();
216 let load_order = prepare(tmp_dir.path());
217
218 let expected_plugin_names = vec!["Blank.esp"];
219 assert_eq!(expected_plugin_names, load_order.active_plugin_names());
220 }
221
222 #[test]
223 fn is_active_should_return_false_for_an_inactive_plugin() {
224 let tmp_dir = tempdir().unwrap();
225 let load_order = prepare(tmp_dir.path());
226
227 assert!(!load_order.is_active("Blank - Different.esp"));
228 }
229
230 #[test]
231 fn is_active_should_return_false_a_plugin_not_in_the_load_order() {
232 let tmp_dir = tempdir().unwrap();
233 let load_order = prepare(tmp_dir.path());
234
235 assert!(!load_order.is_active("missing.esp"));
236 }
237
238 #[test]
239 fn is_active_should_return_true_for_an_active_plugin() {
240 let tmp_dir = tempdir().unwrap();
241 let load_order = prepare(tmp_dir.path());
242
243 assert!(load_order.is_active("Blank.esp"));
244 }
245
246 #[test]
247 fn is_active_should_be_case_insensitive() {
248 let tmp_dir = tempdir().unwrap();
249 let load_order = prepare(tmp_dir.path());
250
251 assert!(load_order.is_active("blank.esp"));
252 }
253}