1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// SPDX-License-Identifier: GPL-3.0-only
//! Virtual file system library for `OpenMW` modding tools.
//!
//! `vfstool_lib` builds a resolved view of an `OpenMW`-style virtual file system from ordered
//! data directories and, when archive features are enabled, BSA/BA2/ZIP/PK3 archives. Paths are
//! normalized to lowercase keys with `/` separators. Directory priority follows `OpenMW`'s
//! `data=` semantics: later loose directories win, and loose files override archive entries.
//!
//! # Stable 1.0 surface
//!
//! Prefer the top-level re-exports for application code:
//!
//! - [`VFS`] for provider stacks, the resolved winner map, and materialization helpers.
//! - [`VfsProvider`] when manually pushing provider-stack entries.
//! - [`LayerIndex`] as the canonical provider-occurrence index and [`ConflictIndex`] as its derived
//! source-level conflict projection, plus reports such as [`ConflictsReport`],
//! [`ShadowedReport`], and [`DiffReport`] for load-order diagnostics.
//! - [`LayerIndex`], [`VfsLock`], [`DriftReport`], and related types for provenance, lock, drift,
//! and semantic conflict workflows.
//! - [`run_setup`], [`run_finalize`], [`snapshot_directory`], and [`changed_files`] for
//! dump-run-collect workflows.
//! - [`normalize_host_path`] and [`normalize_host_path_in_place`] for textual host/source path
//! comparisons. Use [`NormalizedPath`] and [`VfsKeyInput`] for actual VFS keys.
//!
//! [`experimental`] exposes policy helpers, solver code, and knowledge-base helpers that are
//! intentionally unstable. Depending on them is possible, but it is buying the sharp end of the rake
//! intentionally.
//!
//! # Mutation model
//!
//! [`VFS`] stores providers low-to-high priority and caches the current winner for fast lookup.
//! Winner-only operations are named as such: [`VFS::set_winner_file`] and
//! [`VFS::remove_resolved_file`] replace or discard a whole provider stack. Stack-aware operations
//! such as [`VFS::push_provider`] and [`VFS::remove_winner`] preserve lower-priority providers and
//! reveal them when the current winner is removed.
//! Prefix APIs such as [`VFS::paths_with`], [`VFS::remove_provider_prefix`], and
//! [`VFS::remove_resolved_prefix`] match VFS path-component boundaries: `textures` includes
//! `textures/foo.dds`, not `textures2/foo.dds`. Byte-prefix matching is not a filesystem model; it
//! is a small bug generator.
//!
//! With archive features enabled, [`VFS::from_directories`] inserts configured archives below all
//! loose directory providers. Manual archive mutation through `push_archive` is deliberately
//! different: it pushes that archive as the newest highest-priority source.
//! [`LayerIndex`] preserves same-source provider occurrences for provenance; [`ConflictIndex`]
//! intentionally remains a source-vs-source projection rather than reporting a mod as conflicting
//! with itself.
//! The `from_directories*` constructors are best-effort builders that still enforce VFS validity:
//! traversal errors, broken configured archives, unsafe keys, and file/directory key conflicts are
//! skipped. A constructed [`VFS`] is materializable by invariant; if a caller wants diagnostics for
//! skipped input, that belongs in a reporting layer, not in the core VFS constructor.
//!
//! # Examples
//!
//! Build a VFS from two data directories and query the winner. Later directories have higher
//! priority, so the second directory wins when both provide the same normalized key.
//!
//! ```
//! use std::{fs, path::Path};
//! use vfstool_lib::VFS;
//!
//! # fn unique_dir(name: &str) -> std::path::PathBuf {
//! # let dir = std::env::temp_dir().join(format!(
//! # "vfstool_doc_{name}_{}_{}",
//! # std::process::id(),
//! # std::time::SystemTime::now()
//! # .duration_since(std::time::UNIX_EPOCH)
//! # .unwrap()
//! # .as_nanos()
//! # ));
//! # fs::create_dir_all(&dir).unwrap();
//! # dir
//! # }
//! # let low = unique_dir("low");
//! # let high = unique_dir("high");
//! # fs::create_dir_all(low.join("Textures")).unwrap();
//! # fs::create_dir_all(high.join("textures")).unwrap();
//! # fs::write(low.join("Textures/Foo.DDS"), b"low").unwrap();
//! # fs::write(high.join("textures/foo.dds"), b"high").unwrap();
//! let vfs = VFS::from_directories([&low, &high], None);
//!
//! let winner = vfs.get_file("TEXTURES\\FOO.DDS").unwrap();
//! assert_eq!(winner.path(), high.join("textures/foo.dds"));
//! assert!(vfs.contains(Path::new("textures/foo.dds")));
//! # let _ = fs::remove_dir_all(low);
//! # let _ = fs::remove_dir_all(high);
//! ```
//!
//! Ask the provider index why a key resolves the way it does.
//!
//! ```
//! use std::path::{Path, PathBuf};
//! use vfstool_lib::{LayerIndex, SourceKind, SourceMeta};
//!
//! let layer = LayerIndex::from_file_lists([
//! (
//! SourceMeta { path: PathBuf::from("base"), kind: SourceKind::LooseDir },
//! vec![PathBuf::from("textures/foo.dds")],
//! ),
//! (
//! SourceMeta { path: PathBuf::from("mod"), kind: SourceKind::LooseDir },
//! vec![PathBuf::from("textures/foo.dds")],
//! ),
//! ]);
//!
//! let chain = layer.provider_chain(Path::new("textures/foo.dds"));
//! assert_eq!(chain.len(), 2);
//! assert_eq!(chain.last().unwrap().source.path, PathBuf::from("mod"));
//! ```
//!
//! Use semantic analysis for modest, content-aware comparisons. This is not clairvoyance; it is
//! deliberately scoped classification for formats the library understands.
//!
//! ```
//! use vfstool_lib::{analyze_pair, AssetClass, SemanticDelta};
//!
//! let (class, delta) = analyze_pair(
//! std::path::Path::new("settings.ini"),
//! b"[section]\na = 1\nb = 2\n",
//! b"# reordered\n[section]\nb = 2\na = 1\n",
//! );
//!
//! assert_eq!(class, AssetClass::Ini);
//! assert_eq!(delta, SemanticDelta::CosmeticOnly);
//! ```
//!
//! # Feature flags
//!
//! - `beth-archives`: BSA/BA2 archive support.
//! - `zip`: ZIP/PK3 archive support. Entries are buffered on open with a 512 MiB
//! per-entry uncompressed cap; they are not streamed in 1.0, and parallel extraction can buffer
//! multiple entries at once.
//! - `serialize`: JSON/YAML/TOML serialization and structured JSON/TOML semantic comparison.
//! Without `serialize`, JSON and TOML semantic deltas are reported as unknown rather than parsed.
//! This also re-exports [`serde`], [`serde_json`], [`serde_yaml`], and [`toml`] so downstream
//! tools can use the exact serialization stack selected by `vfstool_lib` instead of pinning a
//! parallel set of dependencies. Two TOML parsers in one tool is technically valid. It is also
//! how you get to debug nothing for an afternoon.
//! - `lua`: embedded `mlua` bindings for the promoted stable API surface. This is not a `cdylib`
//! Lua module; hosts register `lua::open` or `lua::register` into their own Lua state.
//! - `standalone-lua`: enables `lua` with vendored `LuaJIT` for standalone embedded hosts.
//!
//! # Runner warning
//!
//! [`run_setup`] may create hardlinks by default. Child tools that edit files in place can mutate
//! original loose source files through those hardlinks. Use copy mode for tools that are not
//! hardlink-safe. This is not a hidden safety feature; it is a tradeoff with teeth.
/// Higher-level analysis APIs: provenance, lock manifests, drift, and semantic conflict reports.
/// Low-level archive loading and enumeration (BSA, BA2, ZIP, PK3).
/// Conflict analysis: per-source override and overridden-by sets.
/// Tree node used for display and serialization of VFS directory structure.
/// Experimental policies, solver, and knowledge-base helpers.
///
/// This namespace is public but unstable: useful for composing advanced workflows, not promoted as
/// frozen 1.0 API.
/// Core shared identifiers and normalized key/digest types.
/// Embedded Lua bindings for the promoted stable API surface.
/// Shared glob/path matching utilities.
/// Path normalization and safety helpers.
/// Report types returned by conflict, shadowed, provider, and diff subcommands.
/// Utilities for the MO2-style `run` workflow: dump, snapshot, and finalize.
/// Semantic analyzers and semantic conflict report types.
/// Core [`VFS`] struct and directory-construction logic.
/// [`VfsFile`] wrapper for loose and archive-backed files.
pub use ;
pub use ;
pub use DirectoryNode;
pub use NormalizedPath;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use VfsFile;
pub use serde;
pub use serde_json;
pub use serde_yaml;
pub use toml;
use ;
/// Sorted map from a directory name to its [`DirectoryNode`], used for display and serialization.
pub type DisplayTree = ;
/// Output format for [`serialize_value`] and [`VFS::serialize_from_tree`].
/// Serialize any `serde::Serialize` value to JSON, YAML, or TOML.
///
/// # Errors
///
/// Returns an error if serialization to the requested format fails.