Create nlohmann serializers for std::optional
and use
This is somewhat tricky.
This commit is contained in:
parent
3b0d8fd796
commit
c8825e9d8c
4 changed files with 97 additions and 25 deletions
|
@ -1,10 +1,9 @@
|
||||||
#include "args.hh"
|
#include "args.hh"
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
|
#include "json-utils.hh"
|
||||||
|
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
void Args::addFlag(Flag && flag_)
|
void Args::addFlag(Flag && flag_)
|
||||||
|
@ -247,11 +246,7 @@ nlohmann::json Args::toJSON()
|
||||||
j["arity"] = flag->handler.arity;
|
j["arity"] = flag->handler.arity;
|
||||||
if (!flag->labels.empty())
|
if (!flag->labels.empty())
|
||||||
j["labels"] = flag->labels;
|
j["labels"] = flag->labels;
|
||||||
// TODO With C++23 use `std::optional::tranform`
|
j["experimental-feature"] = flag->experimentalFeature;
|
||||||
if (auto & xp = flag->experimentalFeature)
|
|
||||||
j["experimental-feature"] = showExperimentalFeature(*xp);
|
|
||||||
else
|
|
||||||
j["experimental-feature"] = nullptr;
|
|
||||||
flags[name] = std::move(j);
|
flags[name] = std::move(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,11 +411,7 @@ nlohmann::json MultiCommand::toJSON()
|
||||||
cat["id"] = command->category();
|
cat["id"] = command->category();
|
||||||
cat["description"] = trim(categories[command->category()]);
|
cat["description"] = trim(categories[command->category()]);
|
||||||
j["category"] = std::move(cat);
|
j["category"] = std::move(cat);
|
||||||
// TODO With C++23 use `std::optional::tranform`
|
cat["experimental-feature"] = command->experimentalFeature();
|
||||||
if (auto xp = command->experimentalFeature())
|
|
||||||
cat["experimental-feature"] = showExperimentalFeature(*xp);
|
|
||||||
else
|
|
||||||
cat["experimental-feature"] = nullptr;
|
|
||||||
cmds[name] = std::move(j);
|
cmds[name] = std::move(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "comparator.hh"
|
#include "comparator.hh"
|
||||||
#include "error.hh"
|
#include "error.hh"
|
||||||
#include "nlohmann/json_fwd.hpp"
|
#include "json-utils.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -93,4 +93,10 @@ public:
|
||||||
void to_json(nlohmann::json &, const ExperimentalFeature &);
|
void to_json(nlohmann::json &, const ExperimentalFeature &);
|
||||||
void from_json(const nlohmann::json &, ExperimentalFeature &);
|
void from_json(const nlohmann::json &, ExperimentalFeature &);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is always rendered as a string
|
||||||
|
*/
|
||||||
|
template<>
|
||||||
|
struct json_avoids_null<ExperimentalFeature> : std::true_type {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
19
src/libutil/json-utils.cc
Normal file
19
src/libutil/json-utils.cc
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include "json-utils.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
const nlohmann::json * get(const nlohmann::json & map, const std::string & key)
|
||||||
|
{
|
||||||
|
auto i = map.find(key);
|
||||||
|
if (i == map.end()) return nullptr;
|
||||||
|
return &*i;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json * get(nlohmann::json & map, const std::string & key)
|
||||||
|
{
|
||||||
|
auto i = map.find(key);
|
||||||
|
if (i == map.end()) return nullptr;
|
||||||
|
return &*i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,21 +2,77 @@
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
const nlohmann::json * get(const nlohmann::json & map, const std::string & key)
|
const nlohmann::json * get(const nlohmann::json & map, const std::string & key);
|
||||||
{
|
|
||||||
auto i = map.find(key);
|
|
||||||
if (i == map.end()) return nullptr;
|
|
||||||
return &*i;
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json * get(nlohmann::json & map, const std::string & key)
|
nlohmann::json * get(nlohmann::json & map, const std::string & key);
|
||||||
{
|
|
||||||
auto i = map.find(key);
|
/**
|
||||||
if (i == map.end()) return nullptr;
|
* For `adl_serializer<std::optional<T>>` below, we need to track what
|
||||||
return &*i;
|
* types are not already using `null`. Only for them can we use `null`
|
||||||
}
|
* to represent `std::nullopt`.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct json_avoids_null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle numbers in default impl
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct json_avoids_null : std::bool_constant<std::is_integral<T>::value> {};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct json_avoids_null<std::nullptr_t> : std::false_type {};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct json_avoids_null<bool> : std::true_type {};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct json_avoids_null<std::string> : std::true_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct json_avoids_null<std::vector<T>> : std::true_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct json_avoids_null<std::list<T>> : std::true_type {};
|
||||||
|
|
||||||
|
template<typename K, typename V>
|
||||||
|
struct json_avoids_null<std::map<K, V>> : std::true_type {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace nlohmann {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This "instance" is widely requested, see
|
||||||
|
* https://github.com/nlohmann/json/issues/1749, but momentum has stalled
|
||||||
|
* out. Writing there here in Nix as a stop-gap.
|
||||||
|
*
|
||||||
|
* We need to make sure the underlying type does not use `null` for this to
|
||||||
|
* round trip. We do that with a static assert.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct adl_serializer<std::optional<T>> {
|
||||||
|
static std::optional<T> from_json(const json & json) {
|
||||||
|
static_assert(
|
||||||
|
nix::json_avoids_null<T>::value,
|
||||||
|
"null is already in use for underlying type's JSON");
|
||||||
|
return json.is_null()
|
||||||
|
? std::nullopt
|
||||||
|
: std::optional { adl_serializer<T>::from_json(json) };
|
||||||
|
}
|
||||||
|
static void to_json(json & json, std::optional<T> t) {
|
||||||
|
static_assert(
|
||||||
|
nix::json_avoids_null<T>::value,
|
||||||
|
"null is already in use for underlying type's JSON");
|
||||||
|
if (t)
|
||||||
|
adl_serializer<T>::to_json(json, *t);
|
||||||
|
else
|
||||||
|
json = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue