aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/json-utils.hh
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/json-utils.hh
parent3b0d8fd796336318ce03c8ee90cd40ebb65fb032 (diff)
Create nlohmann serializers for `std::optional` and use
This is somewhat tricky.
Diffstat (limited to 'src/libutil/json-utils.hh')
-rw-r--r--src/libutil/json-utils.hh78
1 files changed, 67 insertions, 11 deletions
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;
+ }
+};
+
}