Allow to filter sets by release date

This compares the release dates of sets, which enables users to filter
for sets in a certain range, for example to filter for all commanders
with an old card frame, `t:legendary set<8ED` can be used, which will
only include cards appearing before 8th edition.

This acts as a more powerful superset of the "Filter to X most recent
sets" feature.

Fixes #7238
This commit is contained in:
Magnus Groß 2026-09-03 00:54:05 +02:00
parent 4e9d148163
commit 6c2554c445
No known key found for this signature in database
GPG key ID: 0CB6D889DA64A968
3 changed files with 51 additions and 19 deletions

View file

@ -52,6 +52,7 @@ In this list of examples below, each entry has an explanation and can be clicked
<dt><u>E</u>dition:</dt>
<dd>[set:lea](#set:lea) <small>(Cards that appear in Alpha, which has the set code LEA)</small></dd>
<dd>[e:lea OR e:leb](#e:lea OR e:leb) <small>(Cards that appear in Alpha or Beta)</small></dd>
<dd>[e&lt;8ED](#e<8ED) <small>(Cards that appear before 8th edition)</small></dd>
<dt>Negate:</dt>
<dd>[c:wu -c:m](#c:wu -c:m) <small>(Any card that is white or blue, but not multicolored)</small></dd>

View file

@ -5,6 +5,7 @@
#include <QRegularExpression>
#include <QString>
#include <functional>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/utility/peglib.h>
static peg::parser search(R"(
@ -19,7 +20,7 @@ SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart
QueryPart <- NotQuery / SetQuery / RarityQuery / CMCQuery / FormatQuery / PowerQuery / ToughnessQuery / ColorQuery / TypeQuery / OracleQuery / FieldQuery / GenericQuery
NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart
SetQuery <- ('e'/'set') [:] FlexStringValue
SetQuery <- ('e'/'set') SetExpression / ([:] FlexStringValue)
OracleQuery <- 'o' [:] MatcherString
@ -64,6 +65,8 @@ RegexMatcherString <- ('\\/' / !'/' .)+
FlexStringValue <- CompactStringSet / String / [(] StringList [)]
CompactStringSet <- StringListString ([,+] StringListString)+
SetExpression <- NumericOperator ws? String
NumericExpression <- NumericOperator ws? NumericValue
NumericOperator <- [=:] / <[><!][=]?>
NumericValue <- [0-9]+
@ -101,12 +104,25 @@ static void setupParserRules()
return [=](const CardData &x) -> bool { return matcher(x->getCardType()); };
};
search["SetQuery"] = [](const peg::SemanticValues &sv) -> Filter {
auto matcher = std::any_cast<StringMatcher>(sv[0]);
return [=](const CardData &x) -> bool {
QList<QString> sets = x->getSets().keys();
if (sv.choice() == 1) {
auto matcher = std::any_cast<StringMatcher>(sv[0]);
return [=](const CardData &x) -> bool {
QList<QString> sets = x->getSets().keys();
auto matchesSet = [&matcher](const QString &set) { return matcher(set); };
return std::any_of(sets.begin(), sets.end(), matchesSet);
auto matchesSet = [&matcher](const QString &set) { return matcher(set); };
return std::any_of(sets.begin(), sets.end(), matchesSet);
};
}
auto matcher = std::any_cast<NumberMatcher>(sv[0]);
return [=](const CardData &x) -> bool {
const auto &sets = x->getSets().values();
auto matchesSet = [&](const PrintingInfo &printing) {
return printing.getSet()->getEnabled() && matcher(printing.getSet()->getReleaseDate().toJulianDay());
};
return std::any_of(sets.begin(), sets.end(), [&](const auto &printings) {
return std::any_of(printings.begin(), printings.end(), matchesSet);
});
};
};
search["Rarity"] = [](const peg::SemanticValues &sv) -> QString {
@ -247,40 +263,54 @@ static void setupParserRules()
return QString::fromStdString(std::string(sv.sv()));
};
search["NumericExpression"] = [](const peg::SemanticValues &sv) -> NumberMatcher {
const auto arg = std::any_cast<int>(sv[1]);
const auto op = std::any_cast<QString>(sv[0]);
search["NumericOperator"] = [](const peg::SemanticValues &sv) -> NumberComparer {
const auto op = QString::fromStdString(std::string(sv.sv()));
if (op == ">") {
return [=](const int s) { return s > arg; };
return [=](const int s, const int arg) { return s > arg; };
}
if (op == ">=") {
return [=](const int s) { return s >= arg; };
return [=](const int s, const int arg) { return s >= arg; };
}
if (op == "<") {
return [=](const int s) { return s < arg; };
return [=](const int s, const int arg) { return s < arg; };
}
if (op == "<=") {
return [=](const int s) { return s <= arg; };
return [=](const int s, const int arg) { return s <= arg; };
}
if (op == "=") {
return [=](const int s) { return s == arg; };
return [=](const int s, const int arg) { return s == arg; };
}
if (op == ":") {
return [=](const int s) { return s == arg; };
return [=](const int s, const int arg) { return s == arg; };
}
if (op == "!=") {
return [=](const int s) { return s != arg; };
return [=](const int s, const int arg) { return s != arg; };
}
return [](int) { return false; };
return [](int, int) { return false; };
};
search["NumericValue"] = [](const peg::SemanticValues &sv) -> int {
return QString::fromStdString(std::string(sv.sv())).toInt();
};
search["NumericOperator"] = [](const peg::SemanticValues &sv) -> QString {
return QString::fromStdString(std::string(sv.sv()));
search["NumericExpression"] = [](const peg::SemanticValues &sv) -> NumberMatcher {
const auto comparer = std::any_cast<NumberComparer>(sv[0]);
const auto arg = std::any_cast<int>(sv[1]);
return [=](int s) { return comparer(s, arg); };
};
search["SetExpression"] = [](const peg::SemanticValues &sv) -> NumberMatcher {
const auto comparer = std::any_cast<NumberComparer>(sv[0]);
const auto setCode = std::any_cast<QString>(sv[1]);
const auto allSets = CardDatabaseManager::getInstance()->getSetList();
for (auto &set : allSets) {
if (set->getShortName() == setCode) {
const int releaseDate = set->getReleaseDate().toJulianDay();
return [=](int s) { return comparer(s, releaseDate); };
}
}
return [](int) { return false; };
};
search["NormalMatcher"] = [](const peg::SemanticValues &sv) -> StringMatcher {

View file

@ -22,6 +22,7 @@ typedef CardInfoPtr CardData;
typedef std::function<bool(const CardData &)> Filter;
typedef std::function<bool(const QString &)> StringMatcher;
typedef std::function<bool(int)> NumberMatcher;
typedef std::function<bool(int, int)> NumberComparer;
namespace peg
{