Add many_active() metadata condition

It takes a regex and returns true if there is more than one active
plugin matching it, and false otherwise. Closes #522.
This commit is contained in:
Oliver Hamlet
2016-08-06 16:55:16 +01:00
parent f6512c6d35
commit d5803716cd
3 changed files with 77 additions and 0 deletions
+1
View File
@@ -537,6 +537,7 @@ msg:
<tr><td>Checksum<td><code>checksum("<var>filepath</var>", <var>checksum</var>)</code><td>Returns true if the calculated CRC-32 checksum of <var>filepath</var> matches <var>checksum</var>, false otherwise. If <var>filepath</var> does not exist, returns false.
<tr><td>Version<td><code>version("<var>filepath</var>", "<var>version</var>", <var>comparator</var>)</code><td>Returns true if the boolean expression <code>(actual version of <var>filepath</var>) <var>comparator version</var></code> holds true, false otherwise. If <var>filepath</var> does not exist or does not have a version number, its version is assumed to be <code>0</code>. The comparison is not a straightforward per-character comparison, but instead uses the precedence rules defined by <a href="http://semver.org/">Semantic Versioning</a>, extended to allow leading zeroes, an arbitrary number of release version numbers, case-insensitivity and a wider range of separator characters.
<tr><td>Plugin Active Status<td><code>active("<var>filepath</var>")</code><td>Returns true if <var>filepath</var> is a <code>.esp</code> or <code>.esm</code> file that is both installed and active, false otherwise.
<tr><td>Many Plugins Active Status<td><code>many_active("<var>regex</var>")</code><td>Returns true if more than one file matching <var>regex</var> is a <code>.esp</code> or <code>.esm</code> file that is both installed and active, false otherwise.
</table>
<p>The different types of variable used in the above functions are described in more detail below.
+31
View File
@@ -87,6 +87,7 @@ public:
| ("checksum(" > filePath_ > ',' > qi::hex > ')')[phoenix::bind(&ConditionGrammar::CheckSum, this, qi::labels::_val, qi::labels::_1, qi::labels::_2)]
| ("version(" > filePath_ > ',' > quotedStr_ > ',' > comparator_ > ')')[phoenix::bind(&ConditionGrammar::CheckVersion, this, qi::labels::_val, qi::labels::_1, qi::labels::_2, qi::labels::_3)]
| ("active(" > filePath_ > ')')[phoenix::bind(&ConditionGrammar::CheckActive, this, qi::labels::_val, qi::labels::_1)]
| ("many_active(" > quotedStr_ > ')')[phoenix::bind(&ConditionGrammar::CheckManyActive, this, qi::labels::_val, qi::labels::_1)]
;
quotedStr_ %= '"' > +(char_ - '"') > '"';
@@ -376,6 +377,36 @@ private:
BOOST_LOG_TRIVIAL(trace) << "Active check result: " << result;
}
void CheckManyActive(bool& result, const std::string& regexStr) const {
result = false;
BOOST_LOG_TRIVIAL(trace) << "Checking to see if more than one file matching the regex \"" << regexStr << "\" exist.";
std::pair<boost::filesystem::path, std::regex> pathRegex = SplitRegex(regexStr);
if (game_ == nullptr)
return;
// Now we have a valid parent path and a regex filename. Check that
// the parent path exists and is a directory.
boost::filesystem::path parent_path = game_->DataPath() / pathRegex.first;
if (!boost::filesystem::exists(parent_path) || !boost::filesystem::is_directory(parent_path)) {
BOOST_LOG_TRIVIAL(trace) << "The path \"" << parent_path << "\" does not exist or is not a directory.";
return;
}
size_t count = 0;
for (boost::filesystem::directory_iterator itr(parent_path); itr != boost::filesystem::directory_iterator(); ++itr) {
const std::string filename = itr->path().filename().string();
if (std::regex_match(filename, pathRegex.second) && game_->IsPluginActive(filename)) {
++count;
BOOST_LOG_TRIVIAL(trace) << "Matching file found: " << itr->path();
}
}
result = count > 1;
}
void SyntaxError(Iterator const& /*first*/, Iterator const& last, Iterator const& errorpos, boost::spirit::info const& what) {
std::string context(errorpos, last);
boost::trim(context);
@@ -561,6 +561,51 @@ TEST_P(ConditionGrammarTest, anActiveConditionWithAPluginThatIsNotActiveShouldEv
EXPECT_FALSE(result_);
}
TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingMoreThanOnePluginThatIsActiveShouldEvaluateToTrue) {
ASSERT_NO_THROW(game_.Init(false, localPath));
Grammar grammar(&game_);
std::string condition("many_active(\"Blank( - Different Master Dependent)?\\.es(m|p)\")");
success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition),
std::cend(condition),
grammar,
skipper_,
result_);
EXPECT_TRUE(success_);
EXPECT_TRUE(result_);
}
TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingOnlyOnePluginThatIsActiveShouldEvaluateToFalse) {
ASSERT_NO_THROW(game_.Init(false, localPath));
Grammar grammar(&game_);
std::string condition("many_active(\"Blank\\.esm\")");
success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition),
std::cend(condition),
grammar,
skipper_,
result_);
EXPECT_TRUE(success_);
EXPECT_FALSE(result_);
}
TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingNoPluginsThatAreActiveShouldEvaluateToFalse) {
ASSERT_NO_THROW(game_.Init(false, localPath));
Grammar grammar(&game_);
std::string condition("many_active(\"Blank\\.esp\")");
success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition),
std::cend(condition),
grammar,
skipper_,
result_);
EXPECT_TRUE(success_);
EXPECT_FALSE(result_);
}
TEST_P(ConditionGrammarTest, aFalseConditionPrecededByANegatorShouldEvaluateToTrue) {
Grammar grammar(&game_);
std::string condition("not file(\"" + missingEsp + "\")");