aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2023-06-16 15:19:14 -0400
committerJohn Ericson <John.Ericson@Obsidian.Systems>2023-06-18 23:31:10 -0400
commitc8825e9d8c3fa802811f3829d055e3ef9aae90e2 (patch)
tree590b8bf2103bb7e09d2fb72c930fe5542ee3d36d /src/libutil
parent3b0d8fd796336318ce03c8ee90cd40ebb65fb032 (diff)
Create nlohmann serializers for `std::optional` and use
This is somewhat tricky.
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/args.cc15
-rw-r--r--src/libutil/experimental-features.hh8
-rw-r--r--src/libutil/json-utils.cc19
-rw-r--r--src/libutil/json-utils.hh78
4 files changed, 96 insertions, 24 deletions
diff --git a/src/libutil/args.cc b/src/libutil/args.cc
index 081dbeb28..3cf3ed9ca 100644
--- a/src/libutil/args.cc
+++ b/src/libutil/args.cc
@@ -1,10 +1,9 @@
#include "args.hh"
#include "hash.hh"
+#include "json-utils.hh"
#include <glob.h>
-#include <nlohmann/json.hpp>
-
namespace nix {
void Args::addFlag(Flag && flag_)
@@ -247,11 +246,7 @@ nlohmann::json Args::toJSON()
j["arity"] = flag->handler.arity;
if (!flag->labels.empty())
j["labels"] = flag->labels;
- // TODO With C++23 use `std::optional::tranform`
- if (auto & xp = flag->experimentalFeature)
- j["experimental-feature"] = showExperimentalFeature(*xp);
- else
- j["experimental-feature"] = nullptr;
+ j["experimental-feature"] = flag->experimentalFeature;
flags[name] = std::move(j);
}
@@ -416,11 +411,7 @@ nlohmann::json MultiCommand::toJSON()
cat["id"] = command->category();
cat["description"] = trim(categories[command->category()]);
j["category"] = std::move(cat);
- // TODO With C++23 use `std::optional::tranform`
- if (auto xp = command->experimentalFeature())
- cat["experimental-feature"] = showExperimentalFeature(*xp);
- else
- cat["experimental-feature"] = nullptr;
+ cat["experimental-feature"] = command->experimentalFeature();
cmds[name] = std::move(j);
}
diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh
index 892c6c371..15ff5e0cd 100644
--- a/src/libutil/experimental-features.hh
+++ b/src/libutil/experimental-features.hh
@@ -3,7 +3,7 @@
#include "comparator.hh"
#include "error.hh"
-#include "nlohmann/json_fwd.hpp"
+#include "json-utils.hh"
#include "types.hh"
namespace nix {
@@ -93,4 +93,10 @@ public:
void to_json(nlohmann::json &, const ExperimentalFeature &);
void from_json(const nlohmann::json &, ExperimentalFeature &);
+/**
+ * It is always rendered as a string
+ */
+template<>
+struct json_avoids_null<ExperimentalFeature> : std::true_type {};
+
}
diff --git a/src/libutil/json-utils.cc b/src/libutil/json-utils.cc
new file mode 100644
index 000000000..d7220e71d
--- /dev/null
+++ b/src/libutil/json-utils.cc
@@ -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;
+}
+
+}
diff --git a/src/libutil/json-utils.hh b/src/libutil/json-utils.hh
index eb00e954f..5e63c1af4 100644
--- a/src/libutil/json-utils.hh
+++ b/src/libutil/json-utils.hh
@@ -2,21 +2,77 @@
///@file
#include <nlohmann/json.hpp>
+#include <list>
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;
-}
+const nlohmann::json * get(const nlohmann::json & map, const std::string & key);
+
+nlohmann::json * get(nlohmann::json & map, const std::string & key);
+
+/**
+ * For `adl_serializer<std::optional<T>>` below, we need to track what
+ * 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 {};
-nlohmann::json * get(nlohmann::json & map, const std::string & key)
-{
- auto i = map.find(key);
- if (i == map.end()) return nullptr;
- return &*i;
}
+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;
+ }
+};
+
}