Use std::optional to represent "any value" filters

PR #16460.
This commit is contained in:
Vladimir Golovnev 2022-02-20 13:17:34 +03:00 committed by GitHub
parent c627ed4b6f
commit 2d3ff6a97c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 42 deletions

View file

@ -31,9 +31,9 @@
#include "bittorrent/infohash.h" #include "bittorrent/infohash.h"
#include "bittorrent/torrent.h" #include "bittorrent/torrent.h"
const QString TorrentFilter::AnyCategory; const std::optional<QString> TorrentFilter::AnyCategory;
const TorrentIDSet TorrentFilter::AnyID {{}}; const std::optional<TorrentIDSet> TorrentFilter::AnyID;
const QString TorrentFilter::AnyTag; const std::optional<QString> TorrentFilter::AnyTag;
const TorrentFilter TorrentFilter::DownloadingTorrent(TorrentFilter::Downloading); const TorrentFilter TorrentFilter::DownloadingTorrent(TorrentFilter::Downloading);
const TorrentFilter TorrentFilter::SeedingTorrent(TorrentFilter::Seeding); const TorrentFilter TorrentFilter::SeedingTorrent(TorrentFilter::Seeding);
@ -50,7 +50,8 @@ const TorrentFilter TorrentFilter::ErroredTorrent(TorrentFilter::Errored);
using BitTorrent::Torrent; using BitTorrent::Torrent;
TorrentFilter::TorrentFilter(const Type type, const TorrentIDSet &idSet, const QString &category, const QString &tag) TorrentFilter::TorrentFilter(const Type type, const std::optional<TorrentIDSet> &idSet
, const std::optional<QString> &category, const std::optional<QString> &tag)
: m_type(type) : m_type(type)
, m_category(category) , m_category(category)
, m_tag(tag) , m_tag(tag)
@ -58,7 +59,8 @@ TorrentFilter::TorrentFilter(const Type type, const TorrentIDSet &idSet, const Q
{ {
} }
TorrentFilter::TorrentFilter(const QString &filter, const TorrentIDSet &idSet, const QString &category, const QString &tag) TorrentFilter::TorrentFilter(const QString &filter, const std::optional<TorrentIDSet> &idSet
, const std::optional<QString> &category, const std::optional<QString> &tag)
: m_type(All) : m_type(All)
, m_category(category) , m_category(category)
, m_tag(tag) , m_tag(tag)
@ -110,7 +112,7 @@ bool TorrentFilter::setTypeByName(const QString &filter)
return setType(type); return setType(type);
} }
bool TorrentFilter::setTorrentIDSet(const TorrentIDSet &idSet) bool TorrentFilter::setTorrentIDSet(const std::optional<TorrentIDSet> &idSet)
{ {
if (m_idSet != idSet) if (m_idSet != idSet)
{ {
@ -121,12 +123,9 @@ bool TorrentFilter::setTorrentIDSet(const TorrentIDSet &idSet)
return false; return false;
} }
bool TorrentFilter::setCategory(const QString &category) bool TorrentFilter::setCategory(const std::optional<QString> &category)
{ {
// QString::operator==() doesn't distinguish between empty and null strings. if (m_category != category)
if ((m_category != category)
|| (m_category.isNull() && !category.isNull())
|| (!m_category.isNull() && category.isNull()))
{ {
m_category = category; m_category = category;
return true; return true;
@ -135,12 +134,9 @@ bool TorrentFilter::setCategory(const QString &category)
return false; return false;
} }
bool TorrentFilter::setTag(const QString &tag) bool TorrentFilter::setTag(const std::optional<QString> &tag)
{ {
// QString::operator==() doesn't distinguish between empty and null strings. if (m_tag != tag)
if ((m_tag != tag)
|| (m_tag.isNull() && !tag.isNull())
|| (!m_tag.isNull() && tag.isNull()))
{ {
m_tag = tag; m_tag = tag;
return true; return true;
@ -196,23 +192,28 @@ bool TorrentFilter::matchState(const BitTorrent::Torrent *const torrent) const
bool TorrentFilter::matchHash(const BitTorrent::Torrent *const torrent) const bool TorrentFilter::matchHash(const BitTorrent::Torrent *const torrent) const
{ {
if (m_idSet == AnyID) return true; if (!m_idSet)
return true;
return m_idSet.contains(torrent->id()); return m_idSet->contains(torrent->id());
} }
bool TorrentFilter::matchCategory(const BitTorrent::Torrent *const torrent) const bool TorrentFilter::matchCategory(const BitTorrent::Torrent *const torrent) const
{ {
if (m_category.isNull()) return true; if (!m_category)
return true;
return (torrent->belongsToCategory(m_category)); return (torrent->belongsToCategory(*m_category));
} }
bool TorrentFilter::matchTag(const BitTorrent::Torrent *const torrent) const bool TorrentFilter::matchTag(const BitTorrent::Torrent *const torrent) const
{ {
// Empty tag is a special value to indicate we're filtering for untagged torrents. if (!m_tag)
if (m_tag.isNull()) return true; return true;
if (m_tag.isEmpty()) return torrent->tags().isEmpty();
return (torrent->hasTag(m_tag)); // Empty tag is a special value to indicate we're filtering for untagged torrents.
if (m_tag->isEmpty())
return torrent->tags().isEmpty();
return torrent->hasTag(*m_tag);
} }

View file

@ -28,6 +28,8 @@
#pragma once #pragma once
#include <optional>
#include <QSet> #include <QSet>
#include <QString> #include <QString>
@ -61,9 +63,9 @@ public:
}; };
// These mean any permutation, including no category / tag. // These mean any permutation, including no category / tag.
static const QString AnyCategory; static const std::optional<QString> AnyCategory;
static const TorrentIDSet AnyID; static const std::optional<TorrentIDSet> AnyID;
static const QString AnyTag; static const std::optional<QString> AnyTag;
static const TorrentFilter DownloadingTorrent; static const TorrentFilter DownloadingTorrent;
static const TorrentFilter SeedingTorrent; static const TorrentFilter SeedingTorrent;
@ -80,15 +82,16 @@ public:
TorrentFilter() = default; TorrentFilter() = default;
// category & tags: pass empty string for uncategorized / untagged torrents. // category & tags: pass empty string for uncategorized / untagged torrents.
// Pass null string (QString()) to disable filtering (i.e. all torrents). TorrentFilter(Type type, const std::optional<TorrentIDSet> &idSet = AnyID
TorrentFilter(Type type, const TorrentIDSet &idSet = AnyID, const QString &category = AnyCategory, const QString &tag = AnyTag); , const std::optional<QString> &category = AnyCategory, const std::optional<QString> &tag = AnyTag);
TorrentFilter(const QString &filter, const TorrentIDSet &idSet = AnyID, const QString &category = AnyCategory, const QString &tags = AnyTag); TorrentFilter(const QString &filter, const std::optional<TorrentIDSet> &idSet = AnyID
, const std::optional<QString> &category = AnyCategory, const std::optional<QString> &tags = AnyTag);
bool setType(Type type); bool setType(Type type);
bool setTypeByName(const QString &filter); bool setTypeByName(const QString &filter);
bool setTorrentIDSet(const TorrentIDSet &idSet); bool setTorrentIDSet(const std::optional<TorrentIDSet> &idSet);
bool setCategory(const QString &category); bool setCategory(const std::optional<QString> &category);
bool setTag(const QString &tag); bool setTag(const std::optional<QString> &tag);
bool match(const BitTorrent::Torrent *torrent) const; bool match(const BitTorrent::Torrent *torrent) const;
@ -99,7 +102,7 @@ private:
bool matchTag(const BitTorrent::Torrent *torrent) const; bool matchTag(const BitTorrent::Torrent *torrent) const;
Type m_type {All}; Type m_type {All};
QString m_category; std::optional<QString> m_category;
QString m_tag; std::optional<QString> m_tag;
TorrentIDSet m_idSet; std::optional<TorrentIDSet> m_idSet;
}; };

View file

@ -140,6 +140,15 @@ namespace
} }
} }
std::optional<QString> getOptionalString(const StringMap &params, const QString &name)
{
const auto it = params.constFind(name);
if (it == params.cend())
return std::nullopt;
return it.value();
}
QJsonArray getStickyTrackers(const BitTorrent::Torrent *const torrent) QJsonArray getStickyTrackers(const BitTorrent::Torrent *const torrent)
{ {
int seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, leechesDHT = 0, leechesPeX = 0, leechesLSD = 0; int seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, leechesDHT = 0, leechesPeX = 0, leechesLSD = 0;
@ -255,19 +264,23 @@ namespace
void TorrentsController::infoAction() void TorrentsController::infoAction()
{ {
const QString filter {params()["filter"]}; const QString filter {params()["filter"]};
const QString category {params()["category"]}; const std::optional<QString> category = getOptionalString(params(), QLatin1String("category"));
const QString tag {params()["tag"]}; const std::optional<QString> tag = getOptionalString(params(), QLatin1String("tag"));
const QString sortedColumn {params()["sort"]}; const QString sortedColumn {params()["sort"]};
const bool reverse {parseBool(params()["reverse"]).value_or(false)}; const bool reverse {parseBool(params()["reverse"]).value_or(false)};
int limit {params()["limit"].toInt()}; int limit {params()["limit"].toInt()};
int offset {params()["offset"].toInt()}; int offset {params()["offset"].toInt()};
const QStringList hashes {params()["hashes"].split('|', Qt::SkipEmptyParts)}; const QStringList hashes {params()["hashes"].split('|', Qt::SkipEmptyParts)};
TorrentIDSet idSet; std::optional<TorrentIDSet> idSet;
if (!hashes.isEmpty())
{
idSet = TorrentIDSet();
for (const QString &hash : hashes) for (const QString &hash : hashes)
idSet.insert(BitTorrent::TorrentID::fromString(hash)); idSet->insert(BitTorrent::TorrentID::fromString(hash));
}
const TorrentFilter torrentFilter(filter, (hashes.isEmpty() ? TorrentFilter::AnyID : idSet), category, tag); const TorrentFilter torrentFilter {filter, idSet, category, tag};
QVariantList torrentList; QVariantList torrentList;
for (const BitTorrent::Torrent *torrent : asConst(BitTorrent::Session::instance()->torrents())) for (const BitTorrent::Torrent *torrent : asConst(BitTorrent::Session::instance()->torrents()))
{ {