libutil: disallow enum serialization by default
allow opting in to serialization as integers via a trait type instead, and add string-list serializers for the feature flag set enumerations. fixes #738 Change-Id: I2746eb5ef1f15c01b4e681f9ba1615b6c6e64f44
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
synopsis: "Fix experimental and deprecated features showing as integers in `nix config show --json`"
|
||||
issues: [fj#738]
|
||||
cls: [2882]
|
||||
category: "Fixes"
|
||||
credits: ["horrors"]
|
||||
---
|
||||
|
||||
Internal changes in 2.92 caused `nix config show --json` to show deprecated and experimental features not as the list of named features 2.91 and earlier produced, but as integers. This has been fixed.
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lix/libutil/archive.hh"
|
||||
#include "lix/libutil/async-io.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/json-fwd.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/libstore/nar-info.hh"
|
||||
#include "lix/libstore/realisation.hh"
|
||||
@@ -108,6 +109,9 @@ BuildMode buildModeFromInteger(int);
|
||||
|
||||
enum TrustedFlag : bool { NotTrusted = false, Trusted = true };
|
||||
|
||||
template<>
|
||||
struct json::is_integral_enum<TrustedFlag> : std::true_type {};
|
||||
|
||||
struct BuildResult;
|
||||
struct KeyedBuildResult;
|
||||
|
||||
|
||||
@@ -102,4 +102,20 @@ void from_json(const JSON & j, DeprecatedFeature & feature)
|
||||
throw Error("Unknown deprecated feature '%s' in JSON input", input);
|
||||
}
|
||||
|
||||
void to_json(JSON & j, const DeprecatedFeatures & f)
|
||||
{
|
||||
StringSet res;
|
||||
for (auto & depFeature : depFeatureDetails) {
|
||||
if ((f & depFeature.tag) == (DeprecatedFeatures{} | depFeature.tag)) {
|
||||
res.emplace(depFeature.name);
|
||||
}
|
||||
}
|
||||
j = res;
|
||||
}
|
||||
|
||||
void from_json(const JSON & j, DeprecatedFeatures & f)
|
||||
{
|
||||
f = parseDeprecatedFeatures(j.get<StringSet>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/json-fwd.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include <type_traits>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -19,6 +20,10 @@ enum struct DeprecatedFeature
|
||||
NumDepFeatures, // number of available deprecated features, do not use
|
||||
};
|
||||
|
||||
template<>
|
||||
struct json::avoids_null<DeprecatedFeature> : std::true_type
|
||||
{};
|
||||
|
||||
enum struct DeprecatedFeatures {};
|
||||
|
||||
inline DeprecatedFeatures operator| (DeprecatedFeatures a, DeprecatedFeatures b) {
|
||||
@@ -78,6 +83,9 @@ JSON documentDeprecatedFeatures();
|
||||
void to_json(JSON &, const DeprecatedFeature &);
|
||||
void from_json(const JSON &, DeprecatedFeature &);
|
||||
|
||||
void to_json(JSON &, const DeprecatedFeatures &);
|
||||
void from_json(const JSON &, DeprecatedFeatures &);
|
||||
|
||||
/**
|
||||
* A deprecated feature used for some
|
||||
* operation, but was not enabled.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* See libutil/tests/logging.cc for usage examples.
|
||||
*/
|
||||
|
||||
#include "lix/libutil/json-fwd.hh"
|
||||
#include "lix/libutil/suggestions.hh"
|
||||
#include "lix/libutil/fmt.hh"
|
||||
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -42,6 +44,9 @@ typedef enum {
|
||||
lvlVomit
|
||||
} Verbosity;
|
||||
|
||||
template<>
|
||||
struct json::is_integral_enum<Verbosity> : std::true_type {};
|
||||
|
||||
Verbosity verbosityFromIntClamped(int val);
|
||||
|
||||
/**
|
||||
|
||||
@@ -102,4 +102,20 @@ void from_json(const JSON & j, ExperimentalFeature & feature)
|
||||
throw Error("Unknown experimental feature '%s' in JSON input", input);
|
||||
}
|
||||
|
||||
void to_json(JSON & j, const ExperimentalFeatures & f)
|
||||
{
|
||||
StringSet res;
|
||||
for (auto & xpFeature : xpFeatureDetails) {
|
||||
if ((f & xpFeature.tag) == (ExperimentalFeatures{} | xpFeature.tag)) {
|
||||
res.emplace(xpFeature.name);
|
||||
}
|
||||
}
|
||||
j = res;
|
||||
}
|
||||
|
||||
void from_json(const JSON & j, ExperimentalFeatures & f)
|
||||
{
|
||||
f = parseFeatures(j.get<StringSet>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/json-fwd.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include <type_traits>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -16,6 +17,10 @@ enum struct ExperimentalFeature
|
||||
NumXpFeatures, // number of available experimental features, do not use
|
||||
};
|
||||
|
||||
template<>
|
||||
struct json::avoids_null<ExperimentalFeature> : std::true_type
|
||||
{};
|
||||
|
||||
enum struct ExperimentalFeatures {};
|
||||
|
||||
inline ExperimentalFeatures operator| (ExperimentalFeatures a, ExperimentalFeatures b) {
|
||||
@@ -75,6 +80,9 @@ JSON documentExperimentalFeatures();
|
||||
void to_json(JSON &, const ExperimentalFeature &);
|
||||
void from_json(const JSON &, ExperimentalFeature &);
|
||||
|
||||
void to_json(JSON &, const ExperimentalFeatures &);
|
||||
void from_json(const JSON &, ExperimentalFeatures &);
|
||||
|
||||
/**
|
||||
* An experimental feature was required for some (experimental)
|
||||
* operation, but was not enabled.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
///@file Lix-specific JSON handling (forward declarations only).
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -16,7 +17,11 @@ template<typename T>
|
||||
struct avoids_null;
|
||||
|
||||
template<typename T>
|
||||
struct is_integral_enum;
|
||||
struct is_integral_enum : std::false_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
concept IntegralEnum = is_integral_enum<T>::value;
|
||||
|
||||
template<typename T = void, typename SFINAE = void>
|
||||
struct adl_serializer;
|
||||
|
||||
+17
-1
@@ -4,6 +4,8 @@
|
||||
/// overrides on out-of-tree users of libutil, as we'd be required to when
|
||||
/// specializing templates in the nlohmann namespace. nlohmann::json can't
|
||||
/// deal with `std::optional<T>` types until 3.11.3 at, and we need those.
|
||||
/// We also customize enum serialization to not automatically cast to int;
|
||||
/// nlohmann can be told to disable this only via a special global define.
|
||||
|
||||
#include "lix/libutil/json-fwd.hh" // IWYU pragma: keep
|
||||
#include <concepts>
|
||||
@@ -30,7 +32,7 @@ namespace json {
|
||||
*/
|
||||
template<typename T>
|
||||
struct avoids_null
|
||||
: std::bool_constant<std::is_integral_v<T> || std::is_floating_point_v<T> || std::is_enum_v<T>>
|
||||
: std::bool_constant<std::is_integral_v<T> || std::is_floating_point_v<T> || IntegralEnum<T>>
|
||||
{};
|
||||
|
||||
template<>
|
||||
@@ -104,6 +106,20 @@ struct adl_serializer<T, void>
|
||||
{
|
||||
return T::from_json(std::forward<Json>(j));
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
requires IntegralEnum<T>
|
||||
static void to_json(Json && json, const T & value)
|
||||
{
|
||||
json = static_cast<std::underlying_type_t<T>>(value);
|
||||
}
|
||||
|
||||
template<typename Json>
|
||||
requires IntegralEnum<T>
|
||||
static void from_json(const Json & json, T & value)
|
||||
{
|
||||
value = static_cast<T>(json.template get<std::underlying_type_t<T>>());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -44,6 +44,9 @@ typedef enum {
|
||||
actBuildWaiting = 111,
|
||||
} ActivityType;
|
||||
|
||||
template<>
|
||||
struct json::is_integral_enum<ActivityType> : std::true_type {};
|
||||
|
||||
typedef enum {
|
||||
/** Fields:
|
||||
* 0: int: bytes linked
|
||||
@@ -82,6 +85,9 @@ typedef enum {
|
||||
resPostBuildLogLine = 107,
|
||||
} ResultType;
|
||||
|
||||
template<>
|
||||
struct json::is_integral_enum<ResultType> : std::true_type {};
|
||||
|
||||
typedef uint64_t ActivityId;
|
||||
|
||||
struct LoggerSettings : Config
|
||||
|
||||
@@ -518,6 +518,9 @@ add_project_arguments(
|
||||
'-Wdeprecated-copy',
|
||||
'-Wignored-qualifiers',
|
||||
'-Werror=suggest-override',
|
||||
# don't let nlohmann automatically cast enums to ints. we don't set this
|
||||
# inside libutil headers to not force this behavior on users of libutil.
|
||||
'-DJSON_DISABLE_ENUM_SERIALIZATION=1',
|
||||
language : 'cpp',
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "lix/libutil/config.hh"
|
||||
#include "lix/libutil/args.hh"
|
||||
#include "lix/libutil/deprecated-features.hh"
|
||||
#include "lix/libutil/experimental-features.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/environment-variables.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
@@ -323,4 +325,23 @@ namespace nix {
|
||||
ASSERT_THROW(config.applyConfig("value == key"), UsageError);
|
||||
ASSERT_THROW(config.applyConfig("value "), UsageError);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Config enums
|
||||
* --------------------------------------------------------------------------*/
|
||||
TEST(Config, regression_738)
|
||||
{
|
||||
// feature enums serialized as integers because bitmaks type serializers
|
||||
// were forgotten and nlohmann casted then to ints automatically. -sigh-
|
||||
ASSERT_EQ(JSON(ExperimentalFeature::AutoAllocateUids).dump(), "\"auto-allocate-uids\"");
|
||||
ASSERT_EQ(
|
||||
JSON((ExperimentalFeatures{} | ExperimentalFeature::AutoAllocateUids)).dump(),
|
||||
"[\"auto-allocate-uids\"]"
|
||||
);
|
||||
|
||||
ASSERT_EQ(JSON(DeprecatedFeature::AncientLet).dump(), "\"ancient-let\"");
|
||||
ASSERT_EQ(
|
||||
JSON((DeprecatedFeatures{} | DeprecatedFeature::AncientLet)).dump(), "[\"ancient-let\"]"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user