forked from lix-project/lix
Factor out MemorySourceAccessor
, implement missing features
The new `MemorySourceAccessor` rather than being a slightly lossy flat map is a complete in-memory model of file system objects. Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
This commit is contained in:
parent
8e222fbb12
commit
9b880e3e29
|
@ -14,7 +14,7 @@ struct SourcePath;
|
||||||
class StorePath;
|
class StorePath;
|
||||||
class Store;
|
class Store;
|
||||||
|
|
||||||
struct InputAccessor : SourceAccessor, std::enable_shared_from_this<InputAccessor>
|
struct InputAccessor : virtual SourceAccessor, std::enable_shared_from_this<InputAccessor>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Return the maximum last-modified time of the files in this
|
* Return the maximum last-modified time of the files in this
|
||||||
|
|
|
@ -1,48 +1,16 @@
|
||||||
#include "memory-input-accessor.hh"
|
#include "memory-input-accessor.hh"
|
||||||
|
#include "memory-source-accessor.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct MemoryInputAccessorImpl : MemoryInputAccessor
|
struct MemoryInputAccessorImpl : MemoryInputAccessor, MemorySourceAccessor
|
||||||
{
|
{
|
||||||
std::map<CanonPath, std::string> files;
|
|
||||||
|
|
||||||
std::string readFile(const CanonPath & path) override
|
|
||||||
{
|
|
||||||
auto i = files.find(path);
|
|
||||||
if (i == files.end())
|
|
||||||
throw Error("file '%s' does not exist", path);
|
|
||||||
return i->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pathExists(const CanonPath & path) override
|
|
||||||
{
|
|
||||||
auto i = files.find(path);
|
|
||||||
return i != files.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Stat> maybeLstat(const CanonPath & path) override
|
|
||||||
{
|
|
||||||
auto i = files.find(path);
|
|
||||||
if (i != files.end())
|
|
||||||
return Stat { .type = tRegular, .isExecutable = false };
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
DirEntries readDirectory(const CanonPath & path) override
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string readLink(const CanonPath & path) override
|
|
||||||
{
|
|
||||||
throw UnimplementedError("MemoryInputAccessor::readLink");
|
|
||||||
}
|
|
||||||
|
|
||||||
SourcePath addFile(CanonPath path, std::string && contents) override
|
SourcePath addFile(CanonPath path, std::string && contents) override
|
||||||
{
|
{
|
||||||
files.emplace(path, std::move(contents));
|
return {
|
||||||
|
ref(shared_from_this()),
|
||||||
return {ref(shared_from_this()), std::move(path)};
|
MemorySourceAccessor::addFile(path, std::move(contents))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
124
src/libutil/memory-source-accessor.cc
Normal file
124
src/libutil/memory-source-accessor.cc
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
#include "memory-source-accessor.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
MemorySourceAccessor::File *
|
||||||
|
MemorySourceAccessor::open(const CanonPath & path, std::optional<File> create)
|
||||||
|
{
|
||||||
|
File * cur = &root;
|
||||||
|
|
||||||
|
bool newF = false;
|
||||||
|
|
||||||
|
for (std::string_view name : path)
|
||||||
|
{
|
||||||
|
auto * curDirP = std::get_if<File::Directory>(&cur->raw);
|
||||||
|
if (!curDirP)
|
||||||
|
return nullptr;
|
||||||
|
auto & curDir = *curDirP;
|
||||||
|
|
||||||
|
auto i = curDir.contents.find(name);
|
||||||
|
if (i == curDir.contents.end()) {
|
||||||
|
if (!create)
|
||||||
|
return nullptr;
|
||||||
|
else {
|
||||||
|
newF = true;
|
||||||
|
i = curDir.contents.insert(i, {
|
||||||
|
std::string { name },
|
||||||
|
File::Directory {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur = &i->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newF && create) *cur = std::move(*create);
|
||||||
|
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MemorySourceAccessor::readFile(const CanonPath & path)
|
||||||
|
{
|
||||||
|
auto * f = open(path, std::nullopt);
|
||||||
|
if (!f)
|
||||||
|
throw Error("file '%s' does not exist", path);
|
||||||
|
if (auto * r = std::get_if<File::Regular>(&f->raw))
|
||||||
|
return r->contents;
|
||||||
|
else
|
||||||
|
throw Error("file '%s' is not a regular file", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemorySourceAccessor::pathExists(const CanonPath & path)
|
||||||
|
{
|
||||||
|
return open(path, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemorySourceAccessor::Stat MemorySourceAccessor::File::lstat() const
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[](const Regular & r) {
|
||||||
|
return Stat {
|
||||||
|
.type = tRegular,
|
||||||
|
.fileSize = r.contents.size(),
|
||||||
|
.isExecutable = r.executable,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[](const Directory &) {
|
||||||
|
return Stat {
|
||||||
|
.type = tDirectory,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[](const Symlink &) {
|
||||||
|
return Stat {
|
||||||
|
.type = tSymlink,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}, this->raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<MemorySourceAccessor::Stat>
|
||||||
|
MemorySourceAccessor::maybeLstat(const CanonPath & path)
|
||||||
|
{
|
||||||
|
const auto * f = open(path, std::nullopt);
|
||||||
|
return f ? std::optional { f->lstat() } : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemorySourceAccessor::DirEntries MemorySourceAccessor::readDirectory(const CanonPath & path)
|
||||||
|
{
|
||||||
|
auto * f = open(path, std::nullopt);
|
||||||
|
if (!f)
|
||||||
|
throw Error("file '%s' does not exist", path);
|
||||||
|
if (auto * d = std::get_if<File::Directory>(&f->raw)) {
|
||||||
|
DirEntries res;
|
||||||
|
for (auto & [name, file] : d->contents)
|
||||||
|
res.insert_or_assign(name, file.lstat().type);
|
||||||
|
return res;
|
||||||
|
} else
|
||||||
|
throw Error("file '%s' is not a directory", path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MemorySourceAccessor::readLink(const CanonPath & path)
|
||||||
|
{
|
||||||
|
auto * f = open(path, std::nullopt);
|
||||||
|
if (!f)
|
||||||
|
throw Error("file '%s' does not exist", path);
|
||||||
|
if (auto * s = std::get_if<File::Symlink>(&f->raw))
|
||||||
|
return s->target;
|
||||||
|
else
|
||||||
|
throw Error("file '%s' is not a symbolic link", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
CanonPath MemorySourceAccessor::addFile(CanonPath path, std::string && contents)
|
||||||
|
{
|
||||||
|
auto * f = open(path, File { File::Regular {} });
|
||||||
|
if (!f)
|
||||||
|
throw Error("file '%s' cannot be made because some parent file is not a directory", path);
|
||||||
|
if (auto * r = std::get_if<File::Regular>(&f->raw))
|
||||||
|
r->contents = std::move(contents);
|
||||||
|
else
|
||||||
|
throw Error("file '%s' is not a regular file", path);
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
74
src/libutil/memory-source-accessor.hh
Normal file
74
src/libutil/memory-source-accessor.hh
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include "source-accessor.hh"
|
||||||
|
#include "variant-wrapper.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An source accessor for an in-memory file system.
|
||||||
|
*/
|
||||||
|
struct MemorySourceAccessor : virtual SourceAccessor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* In addition to being part of the implementation of
|
||||||
|
* `MemorySourceAccessor`, this has a side benefit of nicely
|
||||||
|
* defining what a "file system object" is in Nix.
|
||||||
|
*/
|
||||||
|
struct File {
|
||||||
|
struct Regular {
|
||||||
|
bool executable = false;
|
||||||
|
std::string contents;
|
||||||
|
|
||||||
|
GENERATE_CMP(Regular, me->executable, me->contents);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Directory {
|
||||||
|
using Name = std::string;
|
||||||
|
|
||||||
|
std::map<Name, File, std::less<>> contents;
|
||||||
|
|
||||||
|
GENERATE_CMP(Directory, me->contents);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Symlink {
|
||||||
|
std::string target;
|
||||||
|
|
||||||
|
GENERATE_CMP(Symlink, me->target);
|
||||||
|
};
|
||||||
|
|
||||||
|
using Raw = std::variant<Regular, Directory, Symlink>;
|
||||||
|
Raw raw;
|
||||||
|
|
||||||
|
MAKE_WRAPPER_CONSTRUCTOR(File);
|
||||||
|
|
||||||
|
GENERATE_CMP(File, me->raw);
|
||||||
|
|
||||||
|
Stat lstat() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
File root { File::Directory {} };
|
||||||
|
|
||||||
|
GENERATE_CMP(MemorySourceAccessor, me->root);
|
||||||
|
|
||||||
|
std::string readFile(const CanonPath & path) override;
|
||||||
|
bool pathExists(const CanonPath & path) override;
|
||||||
|
std::optional<Stat> maybeLstat(const CanonPath & path) override;
|
||||||
|
DirEntries readDirectory(const CanonPath & path) override;
|
||||||
|
std::string readLink(const CanonPath & path) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param create If present, create this file and any parent directories
|
||||||
|
* that are needed.
|
||||||
|
*
|
||||||
|
* Return null if
|
||||||
|
*
|
||||||
|
* - `create = false`: File does not exist.
|
||||||
|
*
|
||||||
|
* - `create = true`: some parent file was not a dir, so couldn't
|
||||||
|
* look/create inside.
|
||||||
|
*/
|
||||||
|
File * open(const CanonPath & path, std::optional<File> create);
|
||||||
|
|
||||||
|
CanonPath addFile(CanonPath path, std::string && contents);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue