11 namespace fsys = std::filesystem;
15 std::unique_lock lock{other._mtx};
16 _root = std::move(other._root);
17 _index = std::move(other._index);
22 std::scoped_lock lock{_mtx, other._mtx};
23 _root = std::move(other._root);
24 _index = std::move(other._index);
31 fsys::create_directories(
root, ec);
34 std::format(
"cannot create project directory '{}': {}",
root.string(), ec.message()));
39 std::format(
"cannot resolve project directory '{}': {}",
root.string(), ec.message()));
42 result._root = std::move(
root);
43 result._indexTreeLocked();
47 void ProjectDirectory::_indexTreeLocked() {
51 for (fsys::recursive_directory_iterator it{_root, ec}, end; !ec && it != end; it.increment(ec)) {
52 if (!it->is_regular_file(ec))
continue;
54 auto relative = fsys::relative(it->path(), _root, ec);
57 _index.insert_or_assign(
normalizePath(relative.generic_string()), it->path());
62 std::unique_lock lock{_mtx};
68 std::shared_lock lock{_mtx};
69 const auto it = _index.find(canonical);
70 return it == _index.end() ? std::nullopt : std::optional{it->second};
74 return resolve(path).has_value();
78 const auto onDisk =
resolve(path);
81 std::format(
"'{}' is not in the project directory",
normalizePath(path)));
83 std::ifstream file{*onDisk, std::ios::binary | std::ios::ate};
87 FileBuffer buffer(
static_cast<std::size_t
>(file.tellg()));
89 if (!buffer.empty() && !file.read(
reinterpret_cast<char*
>(buffer.data()),
90 static_cast<std::streamsize
>(buffer.size())))
103 fsys::create_directories(onDisk.parent_path(), ec);
106 std::format(
"cannot create directories for '{}': {}", onDisk.string(), ec.message()));
108 std::ofstream file{onDisk, std::ios::binary | std::ios::trunc};
112 if (!content.empty())
113 file.write(
reinterpret_cast<const char*
>(content.data()),
static_cast<std::streamsize
>(content.size()));
117 std::unique_lock lock{_mtx};
118 _index.insert_or_assign(canonical, onDisk);
123 std::shared_lock lock{_mtx};
124 return _index.size();
Result< void > write(std::string_view path, std::span< const std::byte > content)
Write (create or overwrite) a file under the overlay, creating intermediate directories,...
void rescan()
Rebuild the index from disk, picking up files changed by other tools.
ProjectDirectory()=default
ProjectDirectory & operator=(const ProjectDirectory &)=delete
std::optional< std::filesystem::path > resolve(std::string_view path) const
The real on-disk location of a client-internal path, if the overlay carries it.
static Result< ProjectDirectory > open(std::filesystem::path root)
Open (and index) a project directory; it is created if missing.
Result< FileBuffer > read(std::string_view path) const
Read an overlay file into memory.
bool exists(std::string_view path) const
Whether the overlay carries a file.
const std::filesystem::path & root() const
std::string normalizePath(std::string_view path)
Canonicalize a client-internal file path.
std::expected< T, Error > Result
Every fallible wowlib operation returns Result<T>; bindings translate the error branch into a target-...
std::string toNativeRelative(std::string_view canonical)
Convert a canonical path to a forward-slash relative path for use on the native filesystem (project-d...
std::unexpected< Error > makeError(ErrorCode code, std::string message, std::uint32_t nativeError=0)
Shorthand for constructing the error branch of a Result.
@ FileNotFound
The file exists nowhere in the overlay or storage.
@ InvalidPath
A path that cannot be normalized/used.
@ IoError
Generic filesystem I/O failure (project directory).
std::vector< std::byte > FileBuffer
Owning byte buffer for file contents read out of a client storage.
Client-internal path canonicalization.
The project-directory overlay: a local directory acting as the ultimate patch over any client storage...