features: centralise requirement logic

This has room for conflicting features in future which will be used with
the OIL implementation. For now it makes it much easier to add
requirements.
This commit is contained in:
Jake Hillion 2023-08-03 04:07:41 -07:00 committed by Jake Hillion
parent 2d32a30bb9
commit 3a2cef0372
3 changed files with 61 additions and 33 deletions

View File

@ -15,8 +15,12 @@
*/
#include "Features.h"
#include <glog/logging.h>
#include <cassert>
#include <map>
#include <numeric>
#include <span>
#include <stdexcept>
namespace oi::detail {
@ -57,6 +61,22 @@ std::string_view featureHelp(Feature f) {
}
}
std::span<const Feature> requirements(Feature f) {
switch (f) {
case Feature::TypedDataSegment:
static constexpr std::array tds = {Feature::TypeGraph};
return tds;
case Feature::TreeBuilderTypeChecking:
static constexpr std::array tc = {Feature::TypedDataSegment};
return tc;
case Feature::TreeBuilderV2:
static constexpr std::array tb2 = {Feature::TreeBuilderTypeChecking};
return tb2;
default:
return {};
}
}
} // namespace
Feature featureFromStr(std::string_view str) {
@ -101,4 +121,32 @@ void featuresHelp(std::ostream& out) {
}
}
std::optional<FeatureSet> handleFeatureConflicts(FeatureSet enabled,
const FeatureSet& disabled) {
FeatureSet toCheck = enabled;
while (toCheck.any()) {
for (const auto f : allFeatures) {
if (!toCheck[f])
continue;
toCheck[f] = false;
for (const auto r : requirements(f)) {
if (enabled[r])
continue;
if (disabled[r]) {
LOG(ERROR) << featureToStr(f) << " feature requires "
<< featureToStr(r) << "but it was explicitly disabled!";
return std::nullopt;
}
enabled[r] = true;
toCheck[r] = true;
LOG(WARNING) << featureToStr(f) << " feature requires "
<< featureToStr(r) << ", enabling it now.";
}
}
}
return enabled;
}
} // namespace oi::detail

View File

@ -16,6 +16,7 @@
#pragma once
#include <array>
#include <optional>
#include <ostream>
#include <string_view>
@ -45,10 +46,6 @@ enum class Feature {
#undef X
};
Feature featureFromStr(std::string_view);
const char* featureToStr(Feature);
void featuresHelp(std::ostream& out);
constexpr std::array allFeatures = {
#define X(name, _) Feature::name,
OI_FEATURE_LIST
@ -58,4 +55,10 @@ constexpr std::array allFeatures = {
// Use "size+1" to account for UnknownFeature"
using FeatureSet = EnumBitset<Feature, allFeatures.size() + 1>;
Feature featureFromStr(std::string_view);
const char* featureToStr(Feature);
void featuresHelp(std::ostream& out);
std::optional<FeatureSet> handleFeatureConflicts(FeatureSet enabled,
const FeatureSet& disabled);
} // namespace oi::detail

View File

@ -172,39 +172,16 @@ std::optional<FeatureSet> processConfigFile(
}
}
FeatureSet featuresSet;
FeatureSet enabledFeatures;
FeatureSet disabledFeatures;
for (auto [k, v] : featureMap) {
if (v) {
featuresSet[k] = true;
enabledFeatures[k] = true;
} else {
disabledFeatures[k] = true;
}
}
if (featuresSet[Feature::TreeBuilderTypeChecking] &&
!featuresSet[Feature::TypedDataSegment]) {
if (auto search = featureMap.find(Feature::TypedDataSegment);
search != featureMap.end() && !search->second) {
LOG(ERROR) << "TreeBuilderTypeChecking feature requires TypedDataSegment "
"feature to be enabled but it was explicitly disabled!";
return {};
}
featuresSet[Feature::TypedDataSegment] = true;
LOG(WARNING) << "TreeBuilderTypeChecking feature requires TypedDataSegment "
"feature to be enabled, enabling now.";
}
if (featuresSet[Feature::TypedDataSegment] &&
!featuresSet[Feature::TypeGraph]) {
if (auto search = featureMap.find(Feature::TypeGraph);
search != featureMap.end() && !search->second) {
LOG(ERROR) << "TypedDataSegment feature requires TypeGraph feature to be "
"enabled but it was explicitly disabled!";
return {};
}
featuresSet[Feature::TypeGraph] = true;
LOG(WARNING) << "TypedDataSegment feature requires TypeGraph feature to be "
"enabled, enabling now.";
}
return featuresSet;
return handleFeatureConflicts(enabledFeatures, disabledFeatures);
}
} // namespace oi::detail::utils