• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loot / loot-condition-interpreter / 12774060573

14 Jan 2025 06:28PM UTC coverage: 88.934% (-0.06%) from 88.996%
12774060573

push

github

Ortham
Add support for OpenMW .omwaddon and .omwgame plugins

1 of 5 new or added lines in 3 files covered. (20.0%)

1 existing line in 1 file now uncovered.

3689 of 4148 relevant lines covered (88.93%)

15.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.49
/src/function/path.rs
1
use std::{
2
    ffi::OsStr,
3
    path::{Path, PathBuf},
4
};
5

6
use crate::{GameType, State};
7

8
const GHOST_EXTENSION: &str = "ghost";
9
const GHOST_EXTENSION_WITH_PERIOD: &str = ".ghost";
10

11
fn is_unghosted_plugin_file_extension(game_type: GameType, extension: &OsStr) -> bool {
71✔
12
    extension.eq_ignore_ascii_case("esp")
71✔
13
        || extension.eq_ignore_ascii_case("esm")
55✔
14
        || (game_type.supports_light_plugins() && extension.eq_ignore_ascii_case("esl"))
37✔
15
        || (game_type == GameType::OpenMW
33✔
NEW
16
            && (extension.eq_ignore_ascii_case("omwaddon")
×
NEW
17
                || extension.eq_ignore_ascii_case("omwgame")))
×
18
}
71✔
19

20
fn has_unghosted_plugin_file_extension(game_type: GameType, path: &Path) -> bool {
32✔
21
    match path.extension() {
32✔
22
        Some(ext) => is_unghosted_plugin_file_extension(game_type, ext),
17✔
23
        _ => false,
15✔
24
    }
25
}
32✔
26

27
pub fn has_plugin_file_extension(game_type: GameType, path: &Path) -> bool {
13✔
28
    match path.extension() {
13✔
29
        Some(ext) if ext.eq_ignore_ascii_case(GHOST_EXTENSION) => path
12✔
30
            .file_stem()
3✔
31
            .map(|s| has_unghosted_plugin_file_extension(game_type, Path::new(s)))
3✔
32
            .unwrap_or(false),
3✔
33
        Some(ext) => is_unghosted_plugin_file_extension(game_type, ext),
9✔
34
        _ => false,
1✔
35
    }
36
}
13✔
37

38
fn add_ghost_extension(path: PathBuf) -> PathBuf {
6✔
39
    match path.extension() {
6✔
40
        Some(e) => {
5✔
41
            let mut new_extension = e.to_os_string();
5✔
42
            new_extension.push(GHOST_EXTENSION_WITH_PERIOD);
5✔
43
            path.with_extension(&new_extension)
5✔
44
        }
45
        None => path.with_extension(GHOST_EXTENSION),
1✔
46
    }
47
}
6✔
48

49
pub fn normalise_file_name(game_type: GameType, name: &OsStr) -> &OsStr {
84✔
50
    let path = Path::new(name);
84✔
51
    if path
84✔
52
        .extension()
84✔
53
        .map(|s| s.eq_ignore_ascii_case(GHOST_EXTENSION))
84✔
54
        .unwrap_or(false)
84✔
55
    {
56
        // name ends in .ghost, trim it and then check the file extension.
57
        if let Some(stem) = path.file_stem() {
3✔
58
            if has_unghosted_plugin_file_extension(game_type, Path::new(stem)) {
3✔
59
                return stem;
3✔
60
            }
×
61
        }
×
62
    }
81✔
63

64
    name
81✔
65
}
84✔
66

67
pub fn resolve_path(state: &State, path: &Path) -> PathBuf {
74✔
68
    // First check external data paths, as files there may override files in the main data path.
69
    for data_path in &state.additional_data_paths {
75✔
70
        let mut path = data_path.join(path);
2✔
71

2✔
72
        if path.exists() {
2✔
73
            return path;
1✔
74
        }
1✔
75

1✔
76
        if has_unghosted_plugin_file_extension(state.game_type, &path) {
1✔
77
            path = add_ghost_extension(path);
×
78
        }
1✔
79

80
        if path.exists() {
1✔
81
            return path;
×
82
        }
1✔
83
    }
84

85
    // Now check the main data path.
86
    let path = state.data_path.join(path);
73✔
87

73✔
88
    if !path.exists() && has_unghosted_plugin_file_extension(state.game_type, &path) {
73✔
89
        add_ghost_extension(path)
4✔
90
    } else {
91
        path
69✔
92
    }
93
}
74✔
94

95
#[cfg(test)]
96
mod tests {
97
    use super::*;
98

99
    #[test]
100
    fn is_unghosted_plugin_file_extension_should_be_true_for_esp_for_all_game_types() {
1✔
101
        let extension = OsStr::new("Esp");
1✔
102

1✔
103
        assert!(is_unghosted_plugin_file_extension(
1✔
104
            GameType::Morrowind,
1✔
105
            extension
1✔
106
        ));
1✔
107
        assert!(is_unghosted_plugin_file_extension(
1✔
108
            GameType::Oblivion,
1✔
109
            extension
1✔
110
        ));
1✔
111
        assert!(is_unghosted_plugin_file_extension(
1✔
112
            GameType::Skyrim,
1✔
113
            extension
1✔
114
        ));
1✔
115
        assert!(is_unghosted_plugin_file_extension(
1✔
116
            GameType::SkyrimSE,
1✔
117
            extension
1✔
118
        ));
1✔
119
        assert!(is_unghosted_plugin_file_extension(
1✔
120
            GameType::SkyrimVR,
1✔
121
            extension
1✔
122
        ));
1✔
123
        assert!(is_unghosted_plugin_file_extension(
1✔
124
            GameType::Fallout3,
1✔
125
            extension
1✔
126
        ));
1✔
127
        assert!(is_unghosted_plugin_file_extension(
1✔
128
            GameType::FalloutNV,
1✔
129
            extension
1✔
130
        ));
1✔
131
        assert!(is_unghosted_plugin_file_extension(
1✔
132
            GameType::Fallout4,
1✔
133
            extension
1✔
134
        ));
1✔
135
        assert!(is_unghosted_plugin_file_extension(
1✔
136
            GameType::Fallout4VR,
1✔
137
            extension
1✔
138
        ));
1✔
139
    }
1✔
140

141
    #[test]
142
    fn is_unghosted_plugin_file_extension_should_be_true_for_esm_for_all_game_types() {
1✔
143
        let extension = OsStr::new("Esm");
1✔
144

1✔
145
        assert!(is_unghosted_plugin_file_extension(
1✔
146
            GameType::Morrowind,
1✔
147
            extension
1✔
148
        ));
1✔
149
        assert!(is_unghosted_plugin_file_extension(
1✔
150
            GameType::Oblivion,
1✔
151
            extension
1✔
152
        ));
1✔
153
        assert!(is_unghosted_plugin_file_extension(
1✔
154
            GameType::Skyrim,
1✔
155
            extension
1✔
156
        ));
1✔
157
        assert!(is_unghosted_plugin_file_extension(
1✔
158
            GameType::SkyrimSE,
1✔
159
            extension
1✔
160
        ));
1✔
161
        assert!(is_unghosted_plugin_file_extension(
1✔
162
            GameType::SkyrimVR,
1✔
163
            extension
1✔
164
        ));
1✔
165
        assert!(is_unghosted_plugin_file_extension(
1✔
166
            GameType::Fallout3,
1✔
167
            extension
1✔
168
        ));
1✔
169
        assert!(is_unghosted_plugin_file_extension(
1✔
170
            GameType::FalloutNV,
1✔
171
            extension
1✔
172
        ));
1✔
173
        assert!(is_unghosted_plugin_file_extension(
1✔
174
            GameType::Fallout4,
1✔
175
            extension
1✔
176
        ));
1✔
177
        assert!(is_unghosted_plugin_file_extension(
1✔
178
            GameType::Fallout4VR,
1✔
179
            extension
1✔
180
        ));
1✔
181
    }
1✔
182

183
    #[test]
184
    fn is_unghosted_plugin_file_extension_should_be_true_for_esl_for_tes5se_tes5vr_fo4_and_fo4vr() {
1✔
185
        let extension = OsStr::new("Esl");
1✔
186

1✔
187
        assert!(is_unghosted_plugin_file_extension(
1✔
188
            GameType::SkyrimSE,
1✔
189
            extension
1✔
190
        ));
1✔
191
        assert!(is_unghosted_plugin_file_extension(
1✔
192
            GameType::SkyrimVR,
1✔
193
            extension
1✔
194
        ));
1✔
195
        assert!(is_unghosted_plugin_file_extension(
1✔
196
            GameType::Fallout4,
1✔
197
            extension
1✔
198
        ));
1✔
199
        assert!(is_unghosted_plugin_file_extension(
1✔
200
            GameType::Fallout4VR,
1✔
201
            extension
1✔
202
        ));
1✔
203
    }
1✔
204

205
    #[test]
206
    fn is_unghosted_plugin_file_extension_should_be_false_for_esl_for_tes3_to_5_fo3_and_fonv() {
1✔
207
        let extension = OsStr::new("Esl");
1✔
208

1✔
209
        assert!(!is_unghosted_plugin_file_extension(
1✔
210
            GameType::Morrowind,
1✔
211
            extension
1✔
212
        ));
1✔
213
        assert!(!is_unghosted_plugin_file_extension(
1✔
214
            GameType::Oblivion,
1✔
215
            extension
1✔
216
        ));
1✔
217
        assert!(!is_unghosted_plugin_file_extension(
1✔
218
            GameType::Skyrim,
1✔
219
            extension
1✔
220
        ));
1✔
221
        assert!(!is_unghosted_plugin_file_extension(
1✔
222
            GameType::Fallout3,
1✔
223
            extension
1✔
224
        ));
1✔
225
        assert!(!is_unghosted_plugin_file_extension(
1✔
226
            GameType::FalloutNV,
1✔
227
            extension
1✔
228
        ));
1✔
229
    }
1✔
230

231
    #[test]
232
    fn is_unghosted_plugin_file_extension_should_be_false_for_ghost_for_all_game_types() {
1✔
233
        let extension = OsStr::new("Ghost");
1✔
234

1✔
235
        assert!(!is_unghosted_plugin_file_extension(
1✔
236
            GameType::Morrowind,
1✔
237
            extension
1✔
238
        ));
1✔
239
        assert!(!is_unghosted_plugin_file_extension(
1✔
240
            GameType::Oblivion,
1✔
241
            extension
1✔
242
        ));
1✔
243
        assert!(!is_unghosted_plugin_file_extension(
1✔
244
            GameType::Skyrim,
1✔
245
            extension
1✔
246
        ));
1✔
247
        assert!(!is_unghosted_plugin_file_extension(
1✔
248
            GameType::SkyrimSE,
1✔
249
            extension
1✔
250
        ));
1✔
251
        assert!(!is_unghosted_plugin_file_extension(
1✔
252
            GameType::SkyrimVR,
1✔
253
            extension
1✔
254
        ));
1✔
255
        assert!(!is_unghosted_plugin_file_extension(
1✔
256
            GameType::Fallout3,
1✔
257
            extension
1✔
258
        ));
1✔
259
        assert!(!is_unghosted_plugin_file_extension(
1✔
260
            GameType::FalloutNV,
1✔
261
            extension
1✔
262
        ));
1✔
263
        assert!(!is_unghosted_plugin_file_extension(
1✔
264
            GameType::Fallout4,
1✔
265
            extension
1✔
266
        ));
1✔
267
        assert!(!is_unghosted_plugin_file_extension(
1✔
268
            GameType::Fallout4VR,
1✔
269
            extension
1✔
270
        ));
1✔
271
    }
1✔
272

273
    #[test]
274
    fn is_unghosted_plugin_file_extension_should_be_false_for_non_esp_esm_esl_for_all_game_types() {
1✔
275
        let extension = OsStr::new("txt");
1✔
276

1✔
277
        assert!(!is_unghosted_plugin_file_extension(
1✔
278
            GameType::Morrowind,
1✔
279
            extension
1✔
280
        ));
1✔
281
        assert!(!is_unghosted_plugin_file_extension(
1✔
282
            GameType::Oblivion,
1✔
283
            extension
1✔
284
        ));
1✔
285
        assert!(!is_unghosted_plugin_file_extension(
1✔
286
            GameType::Skyrim,
1✔
287
            extension
1✔
288
        ));
1✔
289
        assert!(!is_unghosted_plugin_file_extension(
1✔
290
            GameType::SkyrimSE,
1✔
291
            extension
1✔
292
        ));
1✔
293
        assert!(!is_unghosted_plugin_file_extension(
1✔
294
            GameType::SkyrimVR,
1✔
295
            extension
1✔
296
        ));
1✔
297
        assert!(!is_unghosted_plugin_file_extension(
1✔
298
            GameType::Fallout3,
1✔
299
            extension
1✔
300
        ));
1✔
301
        assert!(!is_unghosted_plugin_file_extension(
1✔
302
            GameType::FalloutNV,
1✔
303
            extension
1✔
304
        ));
1✔
305
        assert!(!is_unghosted_plugin_file_extension(
1✔
306
            GameType::Fallout4,
1✔
307
            extension
1✔
308
        ));
1✔
309
        assert!(!is_unghosted_plugin_file_extension(
1✔
310
            GameType::Fallout4VR,
1✔
311
            extension
1✔
312
        ));
1✔
313
    }
1✔
314

315
    #[test]
316
    fn has_unghosted_plugin_file_extension_should_return_false_if_the_path_has_no_extension() {
1✔
317
        assert!(!has_unghosted_plugin_file_extension(
1✔
318
            GameType::Skyrim,
1✔
319
            Path::new("file")
1✔
320
        ));
1✔
321
    }
1✔
322

323
    #[test]
324
    fn has_unghosted_plugin_file_extension_should_return_false_if_the_path_has_a_non_plugin_extension(
1✔
325
    ) {
1✔
326
        assert!(!has_unghosted_plugin_file_extension(
1✔
327
            GameType::Skyrim,
1✔
328
            Path::new("plugin.bsa")
1✔
329
        ));
1✔
330
    }
1✔
331

332
    #[test]
333
    fn has_unghosted_plugin_file_extension_should_return_false_if_the_path_has_a_ghosted_plugin_extension(
1✔
334
    ) {
1✔
335
        assert!(!has_unghosted_plugin_file_extension(
1✔
336
            GameType::Skyrim,
1✔
337
            Path::new("plugin.esp.ghost")
1✔
338
        ));
1✔
339
    }
1✔
340

341
    #[test]
342
    fn has_unghosted_plugin_file_extension_should_return_true_if_the_path_has_an_unghosted_plugin_extension(
1✔
343
    ) {
1✔
344
        assert!(has_unghosted_plugin_file_extension(
1✔
345
            GameType::Skyrim,
1✔
346
            Path::new("plugin.esp")
1✔
347
        ));
1✔
348
    }
1✔
349

350
    #[test]
351
    fn has_plugin_file_extension_should_return_true_if_the_path_has_an_unghosted_plugin_extension()
1✔
352
    {
1✔
353
        assert!(has_plugin_file_extension(
1✔
354
            GameType::Skyrim,
1✔
355
            Path::new("plugin.esp")
1✔
356
        ));
1✔
357
    }
1✔
358

359
    #[test]
360
    fn has_plugin_file_extension_should_return_true_if_the_path_has_a_ghosted_plugin_extension() {
1✔
361
        assert!(has_plugin_file_extension(
1✔
362
            GameType::Skyrim,
1✔
363
            Path::new("plugin.esp.Ghost")
1✔
364
        ));
1✔
365
    }
1✔
366

367
    #[test]
368
    fn has_plugin_file_extension_should_return_false_if_the_path_has_a_non_plugin_extension() {
1✔
369
        assert!(!has_plugin_file_extension(
1✔
370
            GameType::Skyrim,
1✔
371
            Path::new("plugin.bsa")
1✔
372
        ));
1✔
373
    }
1✔
374

375
    #[test]
376
    fn has_plugin_file_extension_should_return_false_if_the_path_has_a_ghosted_non_plugin_extension(
1✔
377
    ) {
1✔
378
        assert!(!has_plugin_file_extension(
1✔
379
            GameType::Skyrim,
1✔
380
            Path::new("plugin.bsa.Ghost")
1✔
381
        ));
1✔
382
    }
1✔
383

384
    #[test]
385
    fn has_plugin_file_extension_should_return_false_if_the_path_has_only_ghost_extension() {
1✔
386
        assert!(!has_plugin_file_extension(
1✔
387
            GameType::Skyrim,
1✔
388
            Path::new("plugin.Ghost")
1✔
389
        ));
1✔
390
    }
1✔
391

392
    #[test]
393
    fn has_plugin_file_extension_should_return_false_if_the_path_has_no_extension() {
1✔
394
        assert!(!has_plugin_file_extension(
1✔
395
            GameType::Skyrim,
1✔
396
            Path::new("plugin")
1✔
397
        ));
1✔
398
    }
1✔
399

400
    #[test]
401
    fn add_ghost_extension_should_add_dot_ghost_to_an_existing_extension() {
1✔
402
        let path = add_ghost_extension("plugin.esp".into());
1✔
403
        assert_eq!(PathBuf::from("plugin.esp.ghost"), path);
1✔
404
    }
1✔
405

406
    #[test]
407
    fn add_ghost_extension_should_add_dot_ghost_to_an_a_path_with_no_extension() {
1✔
408
        let path = add_ghost_extension("plugin".into());
1✔
409
        assert_eq!(PathBuf::from("plugin.ghost"), path);
1✔
410
    }
1✔
411

412
    #[test]
413
    fn resolve_path_should_return_the_data_path_prefixed_path_if_it_exists() {
1✔
414
        let data_path = PathBuf::from(".");
1✔
415
        let state = State::new(GameType::Skyrim, data_path.clone());
1✔
416
        let input_path = Path::new("README.md");
1✔
417
        let resolved_path = resolve_path(&state, input_path);
1✔
418

1✔
419
        assert_eq!(data_path.join(input_path), resolved_path);
1✔
420
    }
1✔
421

422
    #[test]
423
    fn resolve_path_should_return_the_data_path_prefixed_path_if_it_does_not_exist_and_is_not_an_unghosted_plugin_filename(
1✔
424
    ) {
1✔
425
        let data_path = PathBuf::from(".");
1✔
426
        let state = State::new(GameType::Skyrim, data_path.clone());
1✔
427
        let input_path = Path::new("plugin.esp.ghost");
1✔
428
        let resolved_path = resolve_path(&state, input_path);
1✔
429

1✔
430
        assert_eq!(data_path.join(input_path), resolved_path);
1✔
431

432
        let input_path = Path::new("file.txt");
1✔
433
        let resolved_path = resolve_path(&state, input_path);
1✔
434

1✔
435
        assert_eq!(data_path.join(input_path), resolved_path);
1✔
436
    }
1✔
437

438
    #[test]
439
    fn resolve_path_should_return_the_given_data_relative_path_plus_a_ghost_extension_if_the_plugin_path_does_not_exist(
1✔
440
    ) {
1✔
441
        let data_path = PathBuf::from(".");
1✔
442
        let state = State::new(GameType::Skyrim, data_path.clone());
1✔
443
        let input_path = Path::new("plugin.esp");
1✔
444
        let resolved_path = resolve_path(&state, input_path);
1✔
445

1✔
446
        assert_eq!(
1✔
447
            data_path.join(input_path.with_extension("esp.ghost")),
1✔
448
            resolved_path
1✔
449
        );
1✔
450
    }
1✔
451

452
    #[test]
453
    fn resolve_path_should_check_external_data_paths_in_order_before_data_path() {
1✔
454
        use std::fs::copy;
455
        use std::fs::create_dir;
456

457
        let tmp_dir = tempfile::tempdir().unwrap();
1✔
458
        let external_data_path_1 = tmp_dir.path().join("Data1");
1✔
459
        let external_data_path_2 = tmp_dir.path().join("Data2");
1✔
460
        let data_path = tmp_dir.path().join("Data3");
1✔
461

1✔
462
        create_dir(&external_data_path_1).unwrap();
1✔
463
        create_dir(&external_data_path_2).unwrap();
1✔
464
        create_dir(&data_path).unwrap();
1✔
465
        copy(
1✔
466
            Path::new("Cargo.toml"),
1✔
467
            external_data_path_2.join("Cargo.toml"),
1✔
468
        )
1✔
469
        .unwrap();
1✔
470
        copy(Path::new("Cargo.toml"), data_path.join("Cargo.toml")).unwrap();
1✔
471

1✔
472
        let mut state = State::new(GameType::Skyrim, data_path);
1✔
473
        state.set_additional_data_paths(vec![external_data_path_1, external_data_path_2.clone()]);
1✔
474

1✔
475
        let input_path = Path::new("Cargo.toml");
1✔
476
        let resolved_path = resolve_path(&state, input_path);
1✔
477

1✔
478
        assert_eq!(external_data_path_2.join(input_path), resolved_path);
1✔
479
    }
1✔
480
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc