Files
lix/src/libutil/experimental-features.cc
T
Alois Wohlschlager 21fc0ddce5 libutil: generate experimental and deprecated features from data
Currently, a bunch of documentation is generated by embedding parts of it in
the nix executable, getting it out again by running it, and then postprocessing
the output. This is bad, since it creates a pointless dependency of the
documentation on the executable, and also makes documentation generation
impossible when cross-compiling.
Instead, both the code and the documentation should be generated from data, see
https://git.lix.systems/lix-project/lix/issues/292 . Here we start applying
this approach to the experimental and deprecated features, which are done in
one go since the technical implementation is very similar.
Of course, the actual benefits are not realised yet, since the offending
pattern is used in several more places. These will be fixed later.

Change-Id: I4c802052cc7e865c61119a34b8f1063c4decc9cb
2024-11-09 16:05:12 +01:00

109 lines
3.2 KiB
C++

#include "experimental-features.hh"
// Required for instances of to_json and from_json for ExperimentalFeature
#include "experimental-features-json.hh"
#include "strings.hh"
#include "nlohmann/json.hpp"
namespace nix {
struct ExperimentalFeatureDetails
{
ExperimentalFeature tag;
std::string_view name;
std::string_view description;
};
/**
* If two different PRs both add an experimental feature, and we just
* used a number for this, we *woudln't* get merge conflict and the
* counter will be incremented once instead of twice, causing a build
* failure.
*
* By instead defining this instead as a dummy bottom experimental
* feature, we do not get this issue.
*/
constexpr size_t numXpFeatures = static_cast<size_t>(Xp::NumXpFeatures);
constexpr std::array<ExperimentalFeatureDetails, numXpFeatures> xpFeatureDetails = {{
#include "experimental-features-impl.gen.inc"
}};
static_assert(
[]() constexpr {
for (auto [index, feature] : enumerate(xpFeatureDetails))
if (index != (size_t)feature.tag)
return false;
return true;
}(),
"array order does not match enum tag order");
const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::string_view & name)
{
using ReverseXpMap = std::map<std::string_view, ExperimentalFeature>;
static std::unique_ptr<ReverseXpMap> reverseXpMap = []() {
auto reverseXpMap = std::make_unique<ReverseXpMap>();
for (auto & xpFeature : xpFeatureDetails)
(*reverseXpMap)[xpFeature.name] = xpFeature.tag;
return reverseXpMap;
}();
if (auto feature = get(*reverseXpMap, name))
return *feature;
else
return std::nullopt;
}
std::string_view showExperimentalFeature(const ExperimentalFeature tag)
{
assert((size_t)tag < xpFeatureDetails.size());
return xpFeatureDetails[(size_t)tag].name;
}
nlohmann::json documentExperimentalFeatures()
{
StringMap res;
for (auto & xpFeature : xpFeatureDetails)
res[std::string { xpFeature.name }] =
trim(stripIndentation(xpFeature.description));
return (nlohmann::json) res;
}
ExperimentalFeatures parseFeatures(const std::set<std::string> & rawFeatures)
{
ExperimentalFeatures res {};
for (auto & rawFeature : rawFeatures)
if (auto feature = parseExperimentalFeature(rawFeature))
res = res | *feature;
return res;
}
MissingExperimentalFeature::MissingExperimentalFeature(ExperimentalFeature feature)
: Error("experimental Lix feature '%1%' is disabled; use '--extra-experimental-features %1%' to override", showExperimentalFeature(feature))
, missingFeature(feature)
{}
std::ostream & operator <<(std::ostream & str, const ExperimentalFeature & feature)
{
return str << showExperimentalFeature(feature);
}
void to_json(nlohmann::json & j, const ExperimentalFeature & feature)
{
j = showExperimentalFeature(feature);
}
void from_json(const nlohmann::json & j, ExperimentalFeature & feature)
{
const std::string input = j;
const auto parsed = parseExperimentalFeature(input);
if (parsed.has_value())
feature = *parsed;
else
throw Error("Unknown experimental feature '%s' in JSON input", input);
}
}