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 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
|
||||
|
|
|
@ -1,48 +1,16 @@
|
|||
#include "memory-input-accessor.hh"
|
||||
#include "memory-source-accessor.hh"
|
||||
|
||||
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
|
||||
{
|
||||
files.emplace(path, std::move(contents));
|
||||
|
||||
return {ref(shared_from_this()), std::move(path)};
|
||||
return {
|
||||
ref(shared_from_this()),
|
||||
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