mirror of
https://github.com/NixOS/nix.git
synced 2025-11-08 19:46:02 +01:00
Merge branch 'json-schema-fso' into HEAD
This commit is contained in:
commit
e79b1f5384
19 changed files with 447 additions and 40 deletions
|
|
@ -34,6 +34,7 @@ mkMesonDerivation (finalAttrs: {
|
|||
(fileset.unions [
|
||||
../../.version
|
||||
# For example JSON
|
||||
../../src/libutil-tests/data/memory-source-accessor
|
||||
../../src/libutil-tests/data/hash
|
||||
../../src/libstore-tests/data/content-address
|
||||
# Too many different types of files to filter for now
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@
|
|||
- [Architecture and Design](architecture/architecture.md)
|
||||
- [Formats and Protocols](protocols/index.md)
|
||||
- [JSON Formats](protocols/json/index.md)
|
||||
- [File System Object](protocols/json/file-system-object.md)
|
||||
- [Hash](protocols/json/hash.md)
|
||||
- [Pseudo Content Address](protocols/json/content-address.md)
|
||||
- [Store Object Info](protocols/json/store-object-info.md)
|
||||
|
|
|
|||
21
doc/manual/source/protocols/json/file-system-object.md
Normal file
21
doc/manual/source/protocols/json/file-system-object.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{{#include file-system-object-v1-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple
|
||||
|
||||
```json
|
||||
{{#include schema/file-system-object-v1/simple.json}}
|
||||
```
|
||||
|
||||
### Complex
|
||||
|
||||
```json
|
||||
{{#include schema/file-system-object-v1/complex.json}}
|
||||
```
|
||||
|
||||
<!--
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for File System Object v1](schema/file-system-object-v1.json)
|
||||
-->
|
||||
|
|
@ -11,6 +11,7 @@ s/\\`/`/g
|
|||
#
|
||||
# As we have more such relative links, more replacements of this nature
|
||||
# should appear below.
|
||||
s^#/\$defs/\(regular\|symlink\|directory\)^In this schema^g
|
||||
s^\(./hash-v1.yaml\)\?#/$defs/algorithm^[JSON format for `Hash`](./hash.html#algorithm)^g
|
||||
s^\(./hash-v1.yaml\)^[JSON format for `Hash`](./hash.html)^g
|
||||
s^\(./content-address-v1.yaml\)\?#/$defs/method^[JSON format for `ContentAddress`](./content-address.html#method)^g
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ json_schema_for_humans = find_program('generate-schema-doc', required : false)
|
|||
json_schema_config = files('json-schema-for-humans-config.yaml')
|
||||
|
||||
schemas = [
|
||||
'file-system-object-v1',
|
||||
'hash-v1',
|
||||
'content-address-v1',
|
||||
'derivation-v3',
|
||||
|
|
|
|||
1
doc/manual/source/protocols/json/schema/file-system-object-v1
Symbolic link
1
doc/manual/source/protocols/json/schema/file-system-object-v1
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../../../../src/libutil-tests/data/memory-source-accessor
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
"$schema": http://json-schema.org/draft-04/schema#
|
||||
"$id": https://nix.dev/manual/nix/latest/protocols/json/schema/file-system-object-v1.json
|
||||
title: File System Object
|
||||
description: |
|
||||
This schema describes the JSON representation of Nix's [File System Object](@docroot@/store/file-system-object.md).
|
||||
|
||||
The schema is recursive because file system objects contain other file system objects.
|
||||
type: object
|
||||
required: ["type"]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum: ["regular", "symlink", "directory"]
|
||||
|
||||
# Enforce conditional structure based on `type`
|
||||
anyOf:
|
||||
- $ref: "#/$defs/regular"
|
||||
required: ["type", "contents"]
|
||||
|
||||
- $ref: "#/$defs/symlink"
|
||||
required: ["type", "target"]
|
||||
|
||||
- $ref: "#/$defs/directory"
|
||||
required: ["type", "contents"]
|
||||
|
||||
"$defs":
|
||||
regular:
|
||||
title: Regular File
|
||||
required: ["contents"]
|
||||
properties:
|
||||
type:
|
||||
const: "regular"
|
||||
contents:
|
||||
type: string
|
||||
description: Base64-encoded file contents
|
||||
executable:
|
||||
type: boolean
|
||||
description: Whether the file is executable.
|
||||
default: false
|
||||
additionalProperties: false
|
||||
|
||||
symlink:
|
||||
title: Symbolic Link
|
||||
required: ["target"]
|
||||
properties:
|
||||
type:
|
||||
const: "symlink"
|
||||
target:
|
||||
type: string
|
||||
description: Target path of the symlink.
|
||||
additionalProperties: false
|
||||
|
||||
directory:
|
||||
title: Directory
|
||||
required: ["contents"]
|
||||
properties:
|
||||
type:
|
||||
const: "directory"
|
||||
contents:
|
||||
type: object
|
||||
description: |
|
||||
Map of names to nested file system objects (for type=directory)
|
||||
additionalProperties:
|
||||
$ref: "#"
|
||||
additionalProperties: false
|
||||
1
src/json-schema-checks/file-system-object
Symbolic link
1
src/json-schema-checks/file-system-object
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../src/libutil-tests/data/memory-source-accessor
|
||||
|
|
@ -20,6 +20,14 @@ schema_dir = meson.current_source_dir() / 'schema'
|
|||
|
||||
# Get all example files
|
||||
schemas = [
|
||||
{
|
||||
'stem' : 'file-system-object',
|
||||
'schema' : schema_dir / 'file-system-object-v1.yaml',
|
||||
'files' : [
|
||||
'simple.json',
|
||||
'complex.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'hash',
|
||||
'schema' : schema_dir / 'hash-v1.yaml',
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ mkMesonDerivation (finalAttrs: {
|
|||
fileset = lib.fileset.unions [
|
||||
../../.version
|
||||
../../doc/manual/source/protocols/json/schema
|
||||
../../src/libutil-tests/data/memory-source-accessor
|
||||
../../src/libutil-tests/data/hash
|
||||
../../src/libstore-tests/data/content-address
|
||||
../../src/libstore-tests/data/derivation
|
||||
|
|
|
|||
24
src/libutil-tests/data/memory-source-accessor/complex.json
Normal file
24
src/libutil-tests/data/memory-source-accessor/complex.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"contents": {
|
||||
"bar": {
|
||||
"contents": {
|
||||
"baz": {
|
||||
"contents": "Z29vZCBkYXksCg==",
|
||||
"executable": true,
|
||||
"type": "regular"
|
||||
},
|
||||
"quux": {
|
||||
"target": "/over/there",
|
||||
"type": "symlink"
|
||||
}
|
||||
},
|
||||
"type": "directory"
|
||||
},
|
||||
"foo": {
|
||||
"contents": "aGVsbG8K",
|
||||
"executable": false,
|
||||
"type": "regular"
|
||||
}
|
||||
},
|
||||
"type": "directory"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"contents": "YXNkZg==",
|
||||
"executable": false,
|
||||
"type": "regular"
|
||||
}
|
||||
|
|
@ -224,42 +224,15 @@ TEST_F(GitTest, tree_sha256_write)
|
|||
});
|
||||
}
|
||||
|
||||
namespace memory_source_accessor {
|
||||
|
||||
extern ref<MemorySourceAccessor> exampleComplex();
|
||||
|
||||
}
|
||||
|
||||
TEST_F(GitTest, both_roundrip)
|
||||
{
|
||||
using File = MemorySourceAccessor::File;
|
||||
|
||||
auto files = make_ref<MemorySourceAccessor>();
|
||||
files->root = File::Directory{
|
||||
.contents{
|
||||
{
|
||||
"foo",
|
||||
File::Regular{
|
||||
.contents = "hello\n\0\n\tworld!",
|
||||
},
|
||||
},
|
||||
{
|
||||
"bar",
|
||||
File::Directory{
|
||||
.contents =
|
||||
{
|
||||
{
|
||||
"baz",
|
||||
File::Regular{
|
||||
.executable = true,
|
||||
.contents = "good day,\n\0\n\tworld!",
|
||||
},
|
||||
},
|
||||
{
|
||||
"quux",
|
||||
File::Symlink{
|
||||
.target = "/over/there",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
auto files = memory_source_accessor::exampleComplex();
|
||||
|
||||
for (const auto hashAlgo : {HashAlgorithm::SHA1, HashAlgorithm::SHA256}) {
|
||||
std::map<Hash, std::string> cas;
|
||||
|
|
|
|||
116
src/libutil-tests/memory-source-accessor.cc
Normal file
116
src/libutil-tests/memory-source-accessor.cc
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#include <string_view>
|
||||
|
||||
#include "nix/util/bytes.hh"
|
||||
#include "nix/util/memory-source-accessor.hh"
|
||||
#include "nix/util/tests/json-characterization.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
namespace memory_source_accessor {
|
||||
|
||||
using File = MemorySourceAccessor::File;
|
||||
|
||||
ref<MemorySourceAccessor> exampleSimple()
|
||||
{
|
||||
auto sc = make_ref<MemorySourceAccessor>();
|
||||
sc->root = File{File::Regular{
|
||||
.executable = false,
|
||||
.contents = to_owned(as_bytes("asdf")),
|
||||
}};
|
||||
return sc;
|
||||
}
|
||||
|
||||
ref<MemorySourceAccessor> exampleComplex()
|
||||
{
|
||||
auto files = make_ref<MemorySourceAccessor>();
|
||||
files->root = File::Directory{
|
||||
.contents{
|
||||
{
|
||||
"foo",
|
||||
File::Regular{
|
||||
.contents = to_owned(as_bytes("hello\n\0\n\tworld!")),
|
||||
},
|
||||
},
|
||||
{
|
||||
"bar",
|
||||
File::Directory{
|
||||
.contents =
|
||||
{
|
||||
{
|
||||
"baz",
|
||||
File::Regular{
|
||||
.executable = true,
|
||||
.contents = to_owned(as_bytes("good day,\n\0\n\tworld!")),
|
||||
},
|
||||
},
|
||||
{
|
||||
"quux",
|
||||
File::Symlink{
|
||||
.target = "/over/there",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
return files;
|
||||
}
|
||||
|
||||
} // namespace memory_source_accessor
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* JSON
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
||||
class MemorySourceAccessorTest : public virtual CharacterizationTest
|
||||
{
|
||||
std::filesystem::path unitTestData = getUnitTestData() / "memory-source-accessor";
|
||||
|
||||
public:
|
||||
|
||||
std::filesystem::path goldenMaster(std::string_view testStem) const override
|
||||
{
|
||||
return unitTestData / testStem;
|
||||
}
|
||||
};
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
struct MemorySourceAccessorJsonTest : MemorySourceAccessorTest,
|
||||
JsonCharacterizationTest<MemorySourceAccessor>,
|
||||
::testing::WithParamInterface<std::pair<std::string_view, MemorySourceAccessor>>
|
||||
{};
|
||||
|
||||
TEST_P(MemorySourceAccessorJsonTest, from_json)
|
||||
{
|
||||
auto & [name, expected] = GetParam();
|
||||
/* Cannot use `readJsonTest` because need to compare `root` field of
|
||||
the source accessors for equality. */
|
||||
readTest(Path{name} + ".json", [&](const auto & encodedRaw) {
|
||||
auto encoded = json::parse(encodedRaw);
|
||||
auto decoded = static_cast<MemorySourceAccessor>(encoded);
|
||||
ASSERT_EQ(decoded.root, expected.root);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_P(MemorySourceAccessorJsonTest, to_json)
|
||||
{
|
||||
auto & [name, value] = GetParam();
|
||||
writeJsonTest(name, value);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
MemorySourceAccessorJSON,
|
||||
MemorySourceAccessorJsonTest,
|
||||
::testing::Values(
|
||||
std::pair{
|
||||
"simple",
|
||||
*memory_source_accessor::exampleSimple(),
|
||||
},
|
||||
std::pair{
|
||||
"complex",
|
||||
*memory_source_accessor::exampleComplex(),
|
||||
}));
|
||||
|
||||
} // namespace nix
|
||||
|
|
@ -63,6 +63,7 @@ sources = files(
|
|||
'json-utils.cc',
|
||||
'logging.cc',
|
||||
'lru-cache.cc',
|
||||
'memory-source-accessor.cc',
|
||||
'monitorfdhup.cc',
|
||||
'nix_api_util.cc',
|
||||
'nix_api_util_internal.cc',
|
||||
|
|
|
|||
41
src/libutil/include/nix/util/bytes.hh
Normal file
41
src/libutil/include/nix/util/bytes.hh
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace nix {
|
||||
|
||||
static inline std::span<const std::byte> as_bytes(std::string_view sv) noexcept
|
||||
{
|
||||
return std::span<const std::byte>{
|
||||
reinterpret_cast<const std::byte *>(sv.data()),
|
||||
sv.size(),
|
||||
};
|
||||
}
|
||||
|
||||
static inline std::vector<std::byte> to_owned(std::span<const std::byte> bytes)
|
||||
{
|
||||
return std::vector<std::byte>{
|
||||
bytes.begin(),
|
||||
bytes.end(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @note this should be avoided, as arbitrary binary data in strings
|
||||
* views, while allowed, is not really proper. Generally this should
|
||||
* only be used as a stop-gap with other definitions that themselves
|
||||
* should be converted to accept `std::span<const std::byte>` or
|
||||
* similar, directly.
|
||||
*/
|
||||
static inline std::string_view to_str(std::span<const std::byte> sp)
|
||||
{
|
||||
return std::string_view{
|
||||
reinterpret_cast<const char *>(sp.data()),
|
||||
sp.size(),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include "nix/util/source-path.hh"
|
||||
#include "nix/util/fs-sink.hh"
|
||||
#include "nix/util/variant-wrapper.hh"
|
||||
#include "nix/util/json-impls.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ struct MemorySourceAccessor : virtual SourceAccessor
|
|||
struct Regular
|
||||
{
|
||||
bool executable = false;
|
||||
std::string contents;
|
||||
std::vector<std::byte> contents;
|
||||
|
||||
bool operator==(const Regular &) const = default;
|
||||
auto operator<=>(const Regular &) const = default;
|
||||
|
|
@ -86,7 +87,13 @@ struct MemorySourceAccessor : virtual SourceAccessor
|
|||
*/
|
||||
File * open(const CanonPath & path, std::optional<File> create);
|
||||
|
||||
SourcePath addFile(CanonPath path, std::string && contents);
|
||||
SourcePath addFile(CanonPath path, std::vector<std::byte> && contents);
|
||||
|
||||
/**
|
||||
* Small wrapper of the other `addFile`, purely for convenience when
|
||||
* the file in question to be added is a string.
|
||||
*/
|
||||
SourcePath addFile(CanonPath path, std::string_view contents);
|
||||
};
|
||||
|
||||
inline bool MemorySourceAccessor::File::Directory::operator==(
|
||||
|
|
@ -121,4 +128,30 @@ struct MemorySink : FileSystemObjectSink
|
|||
void createSymlink(const CanonPath & path, const std::string & target) override;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct json_avoids_null<MemorySourceAccessor::File::Regular> : std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct json_avoids_null<MemorySourceAccessor::File::Directory> : std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct json_avoids_null<MemorySourceAccessor::File::Symlink> : std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct json_avoids_null<MemorySourceAccessor::File> : std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct json_avoids_null<MemorySourceAccessor> : std::true_type
|
||||
{};
|
||||
|
||||
} // namespace nix
|
||||
|
||||
JSON_IMPL(MemorySourceAccessor::File::Regular)
|
||||
JSON_IMPL(MemorySourceAccessor::File::Directory)
|
||||
JSON_IMPL(MemorySourceAccessor::File::Symlink)
|
||||
JSON_IMPL(MemorySourceAccessor::File)
|
||||
JSON_IMPL(MemorySourceAccessor)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ headers = files(
|
|||
'array-from-string-literal.hh',
|
||||
'base-n.hh',
|
||||
'base-nix-32.hh',
|
||||
'bytes.hh',
|
||||
'callback.hh',
|
||||
'canon-path.hh',
|
||||
'checked-arithmetic.hh',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
#include "nix/util/memory-source-accessor.hh"
|
||||
#include "nix/util/base-n.hh"
|
||||
#include "nix/util/bytes.hh"
|
||||
#include "nix/util/json-utils.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
@ -58,7 +61,7 @@ std::string MemorySourceAccessor::readFile(const CanonPath & path)
|
|||
if (!f)
|
||||
throw Error("file '%s' does not exist", path);
|
||||
if (auto * r = std::get_if<File::Regular>(&f->raw))
|
||||
return r->contents;
|
||||
return std::string{to_str(r->contents)};
|
||||
else
|
||||
throw Error("file '%s' is not a regular file", path);
|
||||
}
|
||||
|
|
@ -125,7 +128,7 @@ std::string MemorySourceAccessor::readLink(const CanonPath & path)
|
|||
throw Error("file '%s' is not a symbolic link", path);
|
||||
}
|
||||
|
||||
SourcePath MemorySourceAccessor::addFile(CanonPath path, std::string && contents)
|
||||
SourcePath MemorySourceAccessor::addFile(CanonPath path, std::vector<std::byte> && contents)
|
||||
{
|
||||
// Create root directory automatically if necessary as a convenience.
|
||||
if (!root && !path.isRoot())
|
||||
|
|
@ -142,6 +145,11 @@ SourcePath MemorySourceAccessor::addFile(CanonPath path, std::string && contents
|
|||
return SourcePath{ref(shared_from_this()), path};
|
||||
}
|
||||
|
||||
SourcePath MemorySourceAccessor::addFile(CanonPath path, std::string_view contents)
|
||||
{
|
||||
return addFile(path, to_owned(as_bytes(contents)));
|
||||
}
|
||||
|
||||
using File = MemorySourceAccessor::File;
|
||||
|
||||
void MemorySink::createDirectory(const CanonPath & path)
|
||||
|
|
@ -190,9 +198,10 @@ void CreateMemoryRegularFile::preallocateContents(uint64_t len)
|
|||
regularFile.contents.reserve(len);
|
||||
}
|
||||
|
||||
void CreateMemoryRegularFile::operator()(std::string_view data)
|
||||
void CreateMemoryRegularFile::operator()(std::string_view data_)
|
||||
{
|
||||
regularFile.contents += data;
|
||||
auto data = as_bytes(data_);
|
||||
regularFile.contents.insert(regularFile.contents.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
void MemorySink::createSymlink(const CanonPath & path, const std::string & target)
|
||||
|
|
@ -222,3 +231,106 @@ ref<SourceAccessor> makeEmptySourceAccessor()
|
|||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
namespace nlohmann {
|
||||
|
||||
using namespace nix;
|
||||
|
||||
MemorySourceAccessor::File::Regular adl_serializer<MemorySourceAccessor::File::Regular>::from_json(const json & json)
|
||||
{
|
||||
auto & obj = getObject(json);
|
||||
return MemorySourceAccessor::File::Regular{
|
||||
.executable = getBoolean(valueAt(obj, "executable")),
|
||||
.contents = to_owned(as_bytes(base64::decode(getString(valueAt(obj, "contents"))))),
|
||||
};
|
||||
}
|
||||
|
||||
void adl_serializer<MemorySourceAccessor::File::Regular>::to_json(
|
||||
json & json, const MemorySourceAccessor::File::Regular & val)
|
||||
{
|
||||
json = {
|
||||
{"executable", val.executable},
|
||||
{"contents", base64::encode(val.contents)},
|
||||
};
|
||||
}
|
||||
|
||||
MemorySourceAccessor::File::Directory
|
||||
adl_serializer<MemorySourceAccessor::File::Directory>::from_json(const json & json)
|
||||
{
|
||||
auto & obj = getObject(json);
|
||||
return MemorySourceAccessor::File::Directory{
|
||||
.contents = valueAt(obj, "contents"),
|
||||
};
|
||||
}
|
||||
|
||||
void adl_serializer<MemorySourceAccessor::File::Directory>::to_json(
|
||||
json & json, const MemorySourceAccessor::File::Directory & val)
|
||||
{
|
||||
json = {
|
||||
{"contents", val.contents},
|
||||
};
|
||||
}
|
||||
|
||||
MemorySourceAccessor::File::Symlink adl_serializer<MemorySourceAccessor::File::Symlink>::from_json(const json & json)
|
||||
{
|
||||
auto & obj = getObject(json);
|
||||
return MemorySourceAccessor::File::Symlink{
|
||||
.target = getString(valueAt(obj, "target")),
|
||||
};
|
||||
}
|
||||
|
||||
void adl_serializer<MemorySourceAccessor::File::Symlink>::to_json(
|
||||
json & json, const MemorySourceAccessor::File::Symlink & val)
|
||||
{
|
||||
json = {
|
||||
{"target", val.target},
|
||||
};
|
||||
}
|
||||
|
||||
MemorySourceAccessor::File adl_serializer<MemorySourceAccessor::File>::from_json(const json & json)
|
||||
{
|
||||
auto & obj = getObject(json);
|
||||
auto type = getString(valueAt(obj, "type"));
|
||||
if (type == "regular")
|
||||
return static_cast<MemorySourceAccessor::File::Regular>(json);
|
||||
if (type == "directory")
|
||||
return static_cast<MemorySourceAccessor::File::Directory>(json);
|
||||
if (type == "symlink")
|
||||
return static_cast<MemorySourceAccessor::File::Symlink>(json);
|
||||
else
|
||||
throw Error("unknown type of file '%s'", type);
|
||||
}
|
||||
|
||||
void adl_serializer<MemorySourceAccessor::File>::to_json(json & json, const MemorySourceAccessor::File & val)
|
||||
{
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const MemorySourceAccessor::File::Regular & r) {
|
||||
json = r;
|
||||
json["type"] = "regular";
|
||||
},
|
||||
[&](const MemorySourceAccessor::File::Directory & d) {
|
||||
json = d;
|
||||
json["type"] = "directory";
|
||||
},
|
||||
[&](const MemorySourceAccessor::File::Symlink & s) {
|
||||
json = s;
|
||||
json["type"] = "symlink";
|
||||
},
|
||||
},
|
||||
val.raw);
|
||||
}
|
||||
|
||||
MemorySourceAccessor adl_serializer<MemorySourceAccessor>::from_json(const json & json)
|
||||
{
|
||||
MemorySourceAccessor res;
|
||||
res.root = json;
|
||||
return res;
|
||||
}
|
||||
|
||||
void adl_serializer<MemorySourceAccessor>::to_json(json & json, const MemorySourceAccessor & val)
|
||||
{
|
||||
json = val.root;
|
||||
}
|
||||
|
||||
} // namespace nlohmann
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue