commits
tags
#include "torlinkc/ui/filter.hpp"
#include <algorithm>
#include <cctype>
#include <sstream>
namespace torlinkc::ui {
namespace {
std::string toLower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
return s;
}
std::string trim(const std::string& s) {
auto first = s.find_first_not_of(" \t\r\n");
if (first == std::string::npos) return "";
auto last = s.find_last_not_of(" \t\r\n");
return s.substr(first, last - first + 1);
}
std::vector<std::string> splitWhitespace(const std::string& s) {
std::istringstream iss(s);
std::vector<std::string> out;
std::string tok;
while (iss >> tok) out.push_back(tok);
return out;
}
} // namespace
std::vector<TorrentResult> filterResults(const std::vector<TorrentResult>& list, bool hideDead,
const std::unordered_map<std::string, Source>& sources,
const std::string& textFilter) {
std::vector<TorrentResult> filtered = list;
if (hideDead) {
std::vector<TorrentResult> next;
for (const auto& r : filtered) {
const auto it = sources.find(r.source);
const bool reportsHealth = it == sources.end() || it->second.reportsHealth;
if (r.seeders > 0 || !reportsHealth) next.push_back(r);
}
filtered = std::move(next);
}
const std::string text = toLower(trim(textFilter));
if (text.empty()) return filtered;
const auto tokens = splitWhitespace(text);
std::string normalizedText;
for (std::size_t i = 0; i < tokens.size(); ++i) {
if (i) normalizedText += ' ';
normalizedText += tokens[i];
}
std::vector<std::pair<int, TorrentResult>> scored;
for (const auto& r : filtered) {
const std::string name = toLower(r.name);
bool matchesAll = true;
for (const auto& token : tokens) {
if (name.find(token) == std::string::npos) {
matchesAll = false;
break;
}
}
if (!matchesAll) continue;
int score = 10; // base score for matching all tokens
if (name.find(normalizedText) != std::string::npos) {
score += 50; // exact substring gets the highest boost
} else {
// Boost if tokens appear in the same order.
long long lastIndex = -1;
bool inOrder = true;
for (const auto& token : tokens) {
const auto idx = name.find(token, static_cast<std::size_t>(lastIndex + 1));
if (idx == std::string::npos || static_cast<long long>(idx) < lastIndex) {
inOrder = false;
break;
}
lastIndex = static_cast<long long>(idx);
}
if (inOrder) score += 20;
}
scored.emplace_back(score, r);
}
std::stable_sort(scored.begin(), scored.end(),
[](const auto& a, const auto& b) { return a.first > b.first; });
std::vector<TorrentResult> out;
out.reserve(scored.size());
for (auto& [score, r] : scored) out.push_back(std::move(r));
return out;
}
} // namespace torlinkc::ui