Skip to main content

loadorder/load_order/
readable.rs

1/*
2 * This file is part of libloadorder
3 *
4 * Copyright (C) 2017 Oliver Hamlet
5 *
6 * libloadorder is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * libloadorder is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with libloadorder. If not, see <http://www.gnu.org/licenses/>.
18 */
19use 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::plugin::ActiveState;
96    use crate::tests::copy_to_test_dir;
97
98    struct TestLoadOrder {
99        game_settings: GameSettings,
100        plugins: Vec<Plugin>,
101    }
102
103    impl ReadableLoadOrderBase for TestLoadOrder {
104        fn game_settings_base(&self) -> &GameSettings {
105            &self.game_settings
106        }
107
108        fn plugins(&self) -> &[Plugin] {
109            &self.plugins
110        }
111    }
112
113    fn prepare(game_dir: &Path) -> TestLoadOrder {
114        let mut game_settings = game_settings_for_test(GameId::Oblivion, game_dir);
115        mock_game_files(&mut game_settings);
116
117        let plugins = vec![
118            Plugin::with_active("Blank.esp", &game_settings, ActiveState::ExplicitlyActive)
119                .unwrap(),
120            Plugin::new("Blank - Different.esp", &game_settings).unwrap(),
121        ];
122
123        TestLoadOrder {
124            game_settings,
125            plugins,
126        }
127    }
128
129    fn prepare_with_ghosted_plugin(game_dir: &Path) -> TestLoadOrder {
130        let mut load_order = prepare(game_dir);
131
132        copy_to_test_dir(
133            "Blank - Different.esm",
134            "Blank - Different.esm.ghost",
135            &load_order.game_settings,
136        );
137        load_order.plugins.insert(
138            0,
139            Plugin::new("Blank - Different.esm.ghost", &load_order.game_settings).unwrap(),
140        );
141
142        load_order
143    }
144
145    #[test]
146    fn plugin_names_should_return_filenames_for_plugins_in_load_order() {
147        let tmp_dir = tempdir().unwrap();
148        let load_order = prepare(tmp_dir.path());
149
150        let expected_plugin_names = vec!["Blank.esp", "Blank - Different.esp"];
151        assert_eq!(expected_plugin_names, load_order.plugin_names());
152    }
153
154    #[test]
155    fn plugin_names_should_return_unghosted_filenames() {
156        let tmp_dir = tempdir().unwrap();
157        let load_order = prepare_with_ghosted_plugin(tmp_dir.path());
158
159        let expected_plugin_names = vec![
160            "Blank - Different.esm",
161            "Blank.esp",
162            "Blank - Different.esp",
163        ];
164        assert_eq!(expected_plugin_names, load_order.plugin_names());
165    }
166
167    #[test]
168    fn index_of_should_return_none_if_the_plugin_is_not_in_the_load_order() {
169        let tmp_dir = tempdir().unwrap();
170        let load_order = prepare(tmp_dir.path());
171
172        assert!(load_order.index_of("Blank.esm").is_none());
173    }
174
175    #[test]
176    fn index_of_should_return_some_index_if_the_plugin_is_in_the_load_order() {
177        let tmp_dir = tempdir().unwrap();
178        let load_order = prepare(tmp_dir.path());
179
180        assert_eq!(0, load_order.index_of("Blank.esp").unwrap());
181    }
182
183    #[test]
184    fn index_of_should_be_case_insensitive() {
185        let tmp_dir = tempdir().unwrap();
186        let load_order = prepare(tmp_dir.path());
187
188        assert_eq!(0, load_order.index_of("blank.esp").unwrap());
189    }
190
191    #[test]
192    fn plugin_at_should_return_none_if_given_an_out_of_bounds_index() {
193        let tmp_dir = tempdir().unwrap();
194        let load_order = prepare(tmp_dir.path());
195
196        assert!(load_order.plugin_at(3).is_none());
197    }
198
199    #[test]
200    fn plugin_at_should_return_some_filename_if_given_an_in_bounds_index() {
201        let tmp_dir = tempdir().unwrap();
202        let load_order = prepare(tmp_dir.path());
203
204        assert_eq!("Blank.esp", load_order.plugin_at(0).unwrap());
205    }
206
207    #[test]
208    fn plugin_at_should_return_some_unghosted_filename() {
209        let tmp_dir = tempdir().unwrap();
210        let load_order = prepare_with_ghosted_plugin(tmp_dir.path());
211
212        assert_eq!("Blank - Different.esm", load_order.plugin_at(0).unwrap());
213    }
214
215    #[test]
216    fn active_plugin_names_should_return_filenames_for_active_plugins_in_load_order() {
217        let tmp_dir = tempdir().unwrap();
218        let load_order = prepare(tmp_dir.path());
219
220        let expected_plugin_names = vec!["Blank.esp"];
221        assert_eq!(expected_plugin_names, load_order.active_plugin_names());
222    }
223
224    #[test]
225    fn is_active_should_return_false_for_an_inactive_plugin() {
226        let tmp_dir = tempdir().unwrap();
227        let load_order = prepare(tmp_dir.path());
228
229        assert!(!load_order.is_active("Blank - Different.esp"));
230    }
231
232    #[test]
233    fn is_active_should_return_false_a_plugin_not_in_the_load_order() {
234        let tmp_dir = tempdir().unwrap();
235        let load_order = prepare(tmp_dir.path());
236
237        assert!(!load_order.is_active("missing.esp"));
238    }
239
240    #[test]
241    fn is_active_should_return_true_for_an_active_plugin() {
242        let tmp_dir = tempdir().unwrap();
243        let load_order = prepare(tmp_dir.path());
244
245        assert!(load_order.is_active("Blank.esp"));
246    }
247
248    #[test]
249    fn is_active_should_be_case_insensitive() {
250        let tmp_dir = tempdir().unwrap();
251        let load_order = prepare(tmp_dir.path());
252
253        assert!(load_order.is_active("blank.esp"));
254    }
255}