Skip to content

Commit

Permalink
feat: add support for parenthesis and operators in post-filters (fix #…
Browse files Browse the repository at this point in the history
  • Loading branch information
Bionus committed Dec 7, 2022
1 parent 04690f7 commit e0fa5b8
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 16 deletions.
49 changes: 40 additions & 9 deletions src/lib/src/models/filtering/post-filter.cpp
Original file line number Diff line number Diff line change
@@ -1,32 +1,63 @@
#include "post-filter.h"
#include <QRegularExpression>
#include <QStringList>
#include "filename/ast/filename-node-condition.h"
#include "filename/filename-parser.h"
#include "filename/visitors/filename-condition-visitor.h"
#include "filter.h"
#include "filter-factory.h"
#include "logger.h"


PostFilter::PostFilter(const QStringList &filters)
PostFilter::PostFilter(const QString &filters)
{
for (const QString &filter : filters) {
auto fil = QSharedPointer<Filter>(FilterFactory::build(filter));
if (!fil.isNull()) {
m_filters.append(fil);
if (filters.isEmpty()) {
return;
}

// Use advanced mode if the filter contains a parenthesis or conditional operator
if (filters.contains(QRegularExpression("[()&|]"))) {
FilenameParser parser(filters);
FilenameNodeCondition *ast = parser.parseCondition();
if (!parser.error().isEmpty()) {
log(QString("Error parsing post-filter '%1': %2").arg(filters, parser.error()), Logger::Error);
return;
}
m_ast = ast;
} else {
for (const QString &filter : filters.split(' ', Qt::SkipEmptyParts)) {
auto fil = QSharedPointer<Filter>(FilterFactory::build(filter));
if (!fil.isNull()) {
m_filters.append(fil);
}
}
}
}

int PostFilter::count() const
{
return m_filters.count();
}
PostFilter::PostFilter(const QStringList &filters)
: PostFilter(filters.join(' '))
{}


QStringList PostFilter::match(const QMap<QString, Token> &tokens) const
{
QStringList ret;

// Advanced mode
if (m_ast != nullptr) {
FilenameConditionVisitor conditionVisitor(tokens, nullptr); // TODO(Bionus): properly pass settings
if (!conditionVisitor.run(*m_ast)) {
ret.append(QStringLiteral("post-filter"));
}
}

// Simple mode
for (const auto &filter : m_filters) {
QString err = filter->match(tokens);
if (!err.isEmpty()) {
ret.append(err);
}
}

return ret;
}
5 changes: 4 additions & 1 deletion src/lib/src/models/filtering/post-filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@
#include <QStringList>


struct FilenameNodeCondition;
class Filter;
class QString;
class Token;

class PostFilter
{
public:
explicit PostFilter(const QString &filters);
explicit PostFilter(const QStringList &filters = QStringList());
int count() const;

QStringList match(const QMap<QString, Token> &tokens) const;

private:
QList<QSharedPointer<Filter>> m_filters;
FilenameNodeCondition *m_ast = nullptr;
};

#endif // POST_FILTER_H
21 changes: 15 additions & 6 deletions src/tests/src/models/filtering/post-filter-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,6 @@ TEST_CASE("PostFilter")

auto img = ImageFactory::build(site, details, profile);

SECTION("Count")
{
REQUIRE(PostFilter(QStringList() << "id:<=10000" << "width:>100" << "date:<2017-01-01").count() == 3);
REQUIRE(PostFilter(QStringList() << "" << "id:<=10000").count() == 1);
}

SECTION("FilterNumeric")
{
auto tokens = img->tokens(profile);
Expand Down Expand Up @@ -116,4 +110,19 @@ TEST_CASE("PostFilter")
filters = PostFilter(QStringList() << "-id:<=10000" << "-width:>100" << "-date:<2017-01-01").match(tokens);
REQUIRE(filters == QStringList() << "image's id match" << "image's width match" << "image's date match");
}

SECTION("Advanced mode")
{
auto tokens = img->tokens(profile);

QStringList filters;

// No match
filters = PostFilter("(rating:safe & tag1) | rating:q").match(tokens);
REQUIRE(filters == QStringList());

// All match
filters = PostFilter("(rating:explicit & tag1) | rating:q").match(tokens);
REQUIRE(filters == QStringList() << "post-filter");
}
}

0 comments on commit e0fa5b8

Please sign in to comment.