Skip to content

Commit

Permalink
allow fine-grained parsing for settings
Browse files Browse the repository at this point in the history
  • Loading branch information
HJfod committed Sep 8, 2024
1 parent f1715cd commit 0efde83
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 36 deletions.
138 changes: 126 additions & 12 deletions loader/include/Geode/loader/SettingV3.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,98 @@ namespace geode {
std::shared_ptr<GeodeImpl> m_impl;

protected:
/**
* Only call this function if you aren't going to call
* `parseBaseProperties`, which will call it for you!
* If you don't want to call `parseBaseProperties`, at the very least
* you **must** call this!
* Select which properties you want to parse using the `parseX`
* functions
* @param key The setting's key as defined in `mod.json`
* @param modID The ID of the mod this settings is being parsed for
* @param json The current JSON checking instance being used. This
* should be the JSON object that defines the setting. If you aren't
* using Geode's JSON checking utilities, you can use the other
* overload of `init`
*/
void init(std::string const& key, std::string const& modID, JsonExpectedValue& json);
/**
* Only call this function if you aren't going to call
* `parseBaseProperties`, which will call it for you!
* If you don't want to call `parseBaseProperties`, at the very least
* you **must** call this!
* Select which properties you want to parse using the `parseX`
* functions
* @param key The setting's key as defined in `mod.json`
* @param modID The ID of the mod this settings is being parsed for
* @note If you are using Geode's JSON checking utilities
* (`checkJson` / `JsonExpectedValue`), you should be using the other
* overload that takes a `JsonExpectedValue&`!
*/
void init(std::string const& key, std::string const& modID);
Result<> parseBaseProperties(std::string const& key, std::string const& modID, matjson::Value const& value, bool onlyNameAndDesc = false);
void parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& value, bool onlyNameAndDesc = false);

/**
* Parses the `"name"` and `"description"` keys from the setting's
* definition in `mod.json` (if they exist), so their values can be
* accessed via `getName` and `getDescription`.
* @param json The current JSON checking instance being used. This
* should be the JSON object that defines the setting
* @warning In most cases, you should be using `parseBaseProperties`
* instead to do all of this in one go!
* If you do need the fine-grained control however, make sure to call
* `init` before calling these parsing functions!
*/
void parseNameAndDescription(JsonExpectedValue& json);
/**
* Parses the `"enable-if"` and `"enable-if-description"` keys from
* the setting's definition in `mod.json` (if they exist), so
* `shouldEnable` and `getEnableIfDescription` work.
* @param json The current JSON checking instance being used. This
* should be the JSON object that defines the setting
* @warning In most cases, you should be using `parseBaseProperties`
* instead to do all of this in one go!
* If you do need the fine-grained control however, make sure to call
* `init` before calling these parsing functions!
*/
void parseEnableIf(JsonExpectedValue& json);
/**
* Parses the `"requires-restart"` key from the setting's definition in
* `mod.json` (if they exist), so `requiresRestart` works.
* @param json The current JSON checking instance being used. This
* should be the JSON object that defines the setting
* @warning In most cases, you should be using `parseBaseProperties`
* instead to do all of this in one go!
* If you do need the fine-grained control however, make sure to call
* `init` before calling these parsing functions!
*/
void parseValueProperties(JsonExpectedValue& json);

/**
* Parse all of the base properties such as `"name"` and `"description"`
* for this setting
* @param key The setting's key as defined in `mod.json`
* @param modID The ID of the mod this settings is being parsed for
* @param json The current JSON checking instance being used. If you
* aren't using Geode's JSON checking utilities, use the other overload
* of this function
* @note If you don't want to parse some of the base properties, such as
* `"requires-restart"` (because you're doing a cosmetic setting), then
* you can call `init` instead and then the specific `parseX` functions
*/
void parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& json);
/**
* Parse all of the base properties such as `"name"` and `"description"`
* for this setting
* @param key The setting's key as defined in `mod.json`
* @param modID The ID of the mod this settings is being parsed for
* @param json The JSON value. If you are using Geode's JSON checking
* utilities (`checkJson` / `JsonExpectedValue`), you should use the
* other overload directly!
* @note If you don't want to parse some of the base properties, such as
* `"requires-restart"` (because you're doing a cosmetic setting), then
* you can call `init` instead and then the specific `parseX` functions
*/
Result<> parseBaseProperties(std::string const& key, std::string const& modID, matjson::Value const& json);

/**
* Mark that the value of this setting has changed. This should be
Expand Down Expand Up @@ -80,6 +169,10 @@ namespace geode {
* Whether this setting requires a restart on change
*/
bool requiresRestart() const;
/**
* Get the platforms this setting is available on
*/
std::vector<PlatformID> getPlatforms() const;

virtual bool load(matjson::Value const& json) = 0;
virtual bool save(matjson::Value& json) const = 0;
Expand All @@ -91,9 +184,15 @@ namespace geode {
*/
virtual void reset() = 0;

[[deprecated("This function will be removed alongside legacy settings in 4.0.0!")]]
[[deprecated(
"This function will be removed alongside legacy settings in 4.0.0! "
"You should NOT be implementing it for your own custom setting classes"
)]]
virtual std::optional<Setting> convertToLegacy() const;
[[deprecated("This function will be removed alongside legacy settings in 4.0.0!")]]
[[deprecated(
"This function will be removed alongside legacy settings in 4.0.0! "
"You should NOT be implementing it for your own custom setting classes"
)]]
virtual std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const;
};

Expand Down Expand Up @@ -123,15 +222,17 @@ namespace geode {

protected:
/**
* Parse shared value, including the default value for this setting
* @param key The key of the setting
* @param modID The ID of the mod this setting is being parsed for
* @param json The current JSON checking instance being used. If you
* aren't using Geode's JSON checking utilities, use the other overload
* of this function
* Parses the `"default"` key from the setting's definition in
* `mod.json`. The key may also be defined per-platform, i.e.
* `"default": { "win": ..., "android": ... }`
* @param json The current JSON checking instance being used. This
* should be the JSON object that defines the setting
* @warning In most cases, you should be using `parseBaseProperties`
* instead to do all of this in one go!
* If you do need the fine-grained control however, make sure to call
* `init` before calling these parsing functions!
*/
void parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& json) {
SettingV3::parseBaseProperties(key, modID, json, false);
void parseDefaultValue(JsonExpectedValue& json) {
auto root = json.needs("default");
// Check if this is a platform-specific default value
if (root.isObject() && root.has(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH)) {
Expand All @@ -142,6 +243,19 @@ namespace geode {
}
m_impl->value = m_impl->defaultValue;
}

/**
* Parse shared value, including the default value for this setting
* @param key The key of the setting
* @param modID The ID of the mod this setting is being parsed for
* @param json The current JSON checking instance being used. If you
* aren't using Geode's JSON checking utilities, use the other overload
* of this function
*/
void parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& json) {
SettingV3::parseBaseProperties(key, modID, json);
this->parseDefaultValue(json);
}
/**
* Parse shared value, including the default value for this setting
* @param key The key of the setting
Expand Down
69 changes: 45 additions & 24 deletions loader/src/loader/SettingV3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ class SettingV3::GeodeImpl {
public:
std::string modID;
std::string key;
std::vector<PlatformID> platforms;
std::optional<std::string> name;
std::optional<std::string> description;
std::optional<std::string> enableIf;
Expand All @@ -471,33 +472,49 @@ class SettingV3::GeodeImpl {
SettingV3::SettingV3() : m_impl(std::make_shared<GeodeImpl>()) {}
SettingV3::~SettingV3() = default;

Result<> SettingV3::parseBaseProperties(std::string const& key, std::string const& modID, matjson::Value const& value, bool onlyNameAndDesc) {
auto json = checkJson(value, "SettingV3");
this->parseBaseProperties(key, modID, json, onlyNameAndDesc);
return json.ok();
void SettingV3::init(std::string const& key, std::string const& modID) {
m_impl->key = key;
m_impl->modID = modID;
}
void SettingV3::parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& value, bool onlyNameAndDesc) {
void SettingV3::init(std::string const& key, std::string const& modID, JsonExpectedValue& json) {
this->init(key, modID);
value.needs("type");
value.has("platforms");
value.has("name").into(m_impl->name);
value.has("description").into(m_impl->description);
if (!onlyNameAndDesc) {
value.has("requires-restart").into(m_impl->requiresRestart);
value.has("enable-if")
.template mustBe<std::string>("a valid \"enable-if\" scheme", [this](std::string const& str) -> Result<> {
GEODE_UNWRAP_INTO(auto tree, enable_if_parsing::Parser::parse(str, m_impl->modID));
GEODE_UNWRAP(tree->check());
m_impl->enableIfTree = std::move(tree);
return Ok();
})
.into(m_impl->enableIf);
value.has("enable-if-description").into(m_impl->enableIfDescription);

// Keys every setting must have
json.needs("type");
for (auto& plat : json.has("platforms").items()) {
m_impl->platforms.push_back(PlatformID::from(plat.template get<std::string>()));
}
}
void SettingV3::init(std::string const& key, std::string const& modID) {
m_impl->key = key;
m_impl->modID = modID;

void SettingV3::parseNameAndDescription(JsonExpectedValue& json) {
json.needs("name").into(m_impl->name);
json.has("description").into(m_impl->description);
}
void SettingV3::parseEnableIf(JsonExpectedValue& json) {
json.has("enable-if")
.template mustBe<std::string>("a valid \"enable-if\" scheme", [this](std::string const& str) -> Result<> {
GEODE_UNWRAP_INTO(auto tree, enable_if_parsing::Parser::parse(str, m_impl->modID));
GEODE_UNWRAP(tree->check());
m_impl->enableIfTree = std::move(tree);
return Ok();
})
.into(m_impl->enableIf);
json.has("enable-if-description").into(m_impl->enableIfDescription);
}
void SettingV3::parseValueProperties(JsonExpectedValue& json) {
json.has("requires-restart").into(m_impl->requiresRestart);
}

Result<> SettingV3::parseBaseProperties(std::string const& key, std::string const& modID, matjson::Value const& value) {
auto json = checkJson(value, "SettingV3");
this->parseBaseProperties(key, modID, json);
return json.ok();
}
void SettingV3::parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& json) {
this->init(key, modID, json);
this->parseNameAndDescription(json);
this->parseValueProperties(json);
this->parseEnableIf(json);
}

std::string SettingV3::getKey() const {
Expand Down Expand Up @@ -540,6 +557,9 @@ std::optional<std::string> SettingV3::getEnableIfDescription() const {
bool SettingV3::requiresRestart() const {
return m_impl->requiresRestart;
}
std::vector<PlatformID> SettingV3::getPlatforms() const {
return m_impl->platforms;
}
Mod* SettingV3::getMod() const {
return Loader::get()->getInstalledMod(m_impl->modID);
}
Expand Down Expand Up @@ -573,7 +593,8 @@ TitleSettingV3::TitleSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>())
Result<std::shared_ptr<TitleSettingV3>> TitleSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto ret = std::make_shared<TitleSettingV3>(PrivateMarker());
auto root = checkJson(json, "TitleSettingV3");
ret->parseBaseProperties(key, modID, root, true);
ret->init(key, modID, root);
ret->parseNameAndDescription(root);
root.checkUnknownKeys();
return root.ok(ret);
}
Expand Down

0 comments on commit 0efde83

Please sign in to comment.