From 1c02dd5b9c2b65115c49d2dbed43c01d467f77c2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 Oct 2025 15:49:47 -0400 Subject: [PATCH] Allow for standard nlohmann JSON serializers to take separate XP features I realized that we can actually do this thing, even though it is not what nlohmann expects at all, because the extra parameter has a default argument so nlohmann doesn't need to care. Sneaky! --- src/libstore-tests/derivation.cc | 36 +++++++------------ src/libstore/derivations.cc | 9 ++--- src/libstore/include/nix/store/derivations.hh | 4 +-- .../nix/util/tests/json-characterization.hh | 4 +-- src/libutil/include/nix/util/json-impls.hh | 14 ++++++++ 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/libstore-tests/derivation.cc b/src/libstore-tests/derivation.cc index 65a5d011d..75bf75753 100644 --- a/src/libstore-tests/derivation.cc +++ b/src/libstore-tests/derivation.cc @@ -66,23 +66,17 @@ TEST_F(DynDerivationTest, BadATerm_oldVersionDynDeps) FormatError); } -#define MAKE_OUTPUT_JSON_TEST_P(FIXTURE) \ - TEST_P(FIXTURE, from_json) \ - { \ - const auto & [name, expected] = GetParam(); \ - /* Don't use readJsonTest because we want to check experimental \ - features. */ \ - readTest(Path{"output-"} + name + ".json", [&](const auto & encoded_) { \ - json j = json::parse(encoded_); \ - DerivationOutput got = DerivationOutput::fromJSON(j, mockXpSettings); \ - ASSERT_EQ(got, expected); \ - }); \ - } \ - \ - TEST_P(FIXTURE, to_json) \ - { \ - const auto & [name, value] = GetParam(); \ - writeJsonTest("output-" + name, value); \ +#define MAKE_OUTPUT_JSON_TEST_P(FIXTURE) \ + TEST_P(FIXTURE, from_json) \ + { \ + const auto & [name, expected] = GetParam(); \ + readJsonTest(Path{"output-"} + name, expected, mockXpSettings); \ + } \ + \ + TEST_P(FIXTURE, to_json) \ + { \ + const auto & [name, value] = GetParam(); \ + writeJsonTest("output-" + name, value); \ } struct DerivationOutputJsonTest : DerivationTest, @@ -193,13 +187,7 @@ INSTANTIATE_TEST_SUITE_P( TEST_P(FIXTURE, from_json) \ { \ const auto & drv = GetParam(); \ - /* Don't use readJsonTest because we want to check experimental \ - features. */ \ - readTest(drv.name + ".json", [&](const auto & encoded_) { \ - auto encoded = json::parse(encoded_); \ - Derivation got = Derivation::fromJSON(encoded, mockXpSettings); \ - ASSERT_EQ(got, drv); \ - }); \ + readJsonTest(drv.name, drv, mockXpSettings); \ } \ \ TEST_P(FIXTURE, to_json) \ diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 24dd61807..d39080e08 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -1496,9 +1496,10 @@ namespace nlohmann { using namespace nix; -DerivationOutput adl_serializer::from_json(const json & json) +DerivationOutput +adl_serializer::from_json(const json & json, const ExperimentalFeatureSettings & xpSettings) { - return DerivationOutput::fromJSON(json); + return DerivationOutput::fromJSON(json, xpSettings); } void adl_serializer::to_json(json & json, const DerivationOutput & c) @@ -1506,9 +1507,9 @@ void adl_serializer::to_json(json & json, const DerivationOutp json = c.toJSON(); } -Derivation adl_serializer::from_json(const json & json) +Derivation adl_serializer::from_json(const json & json, const ExperimentalFeatureSettings & xpSettings) { - return Derivation::fromJSON(json); + return Derivation::fromJSON(json, xpSettings); } void adl_serializer::to_json(json & json, const Derivation & c) diff --git a/src/libstore/include/nix/store/derivations.hh b/src/libstore/include/nix/store/derivations.hh index 0dfb80347..45188d6b3 100644 --- a/src/libstore/include/nix/store/derivations.hh +++ b/src/libstore/include/nix/store/derivations.hh @@ -537,5 +537,5 @@ std::string hashPlaceholder(const OutputNameView outputName); } // namespace nix -JSON_IMPL(nix::DerivationOutput) -JSON_IMPL(nix::Derivation) +JSON_IMPL_WITH_XP_FEATURES(nix::DerivationOutput) +JSON_IMPL_WITH_XP_FEATURES(nix::Derivation) diff --git a/src/libutil-test-support/include/nix/util/tests/json-characterization.hh b/src/libutil-test-support/include/nix/util/tests/json-characterization.hh index 5a38b8e2c..d713c615b 100644 --- a/src/libutil-test-support/include/nix/util/tests/json-characterization.hh +++ b/src/libutil-test-support/include/nix/util/tests/json-characterization.hh @@ -24,12 +24,12 @@ struct JsonCharacterizationTest : virtual CharacterizationTest * @param test hook that takes the contents of the file and does the * actual work */ - void readJsonTest(PathView testStem, const T & expected) + void readJsonTest(PathView testStem, const T & expected, auto... args) { using namespace nlohmann; readTest(Path{testStem} + ".json", [&](const auto & encodedRaw) { auto encoded = json::parse(encodedRaw); - T decoded = adl_serializer::from_json(encoded); + T decoded = adl_serializer::from_json(encoded, args...); ASSERT_EQ(decoded, expected); }); } diff --git a/src/libutil/include/nix/util/json-impls.hh b/src/libutil/include/nix/util/json-impls.hh index 751fc410f..802c212e1 100644 --- a/src/libutil/include/nix/util/json-impls.hh +++ b/src/libutil/include/nix/util/json-impls.hh @@ -3,6 +3,8 @@ #include +#include "nix/util/experimental-features.hh" + // Following https://github.com/nlohmann/json#how-can-i-use-get-for-non-default-constructiblenon-copyable-types #define JSON_IMPL(TYPE) \ namespace nlohmann { \ @@ -14,3 +16,15 @@ static void to_json(json & json, const TYPE & t); \ }; \ } + +#define JSON_IMPL_WITH_XP_FEATURES(TYPE) \ + namespace nlohmann { \ + using namespace nix; \ + template<> \ + struct adl_serializer \ + { \ + static TYPE \ + from_json(const json & json, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); \ + static void to_json(json & json, const TYPE & t); \ + }; \ + }