commits
tags
#include "torlinkc/config/trackers.hpp"
#include <regex>
#include <unordered_set>
namespace torlinkc {
namespace {
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> splitOnCommaOrWhitespace(const std::string& s) {
std::vector<std::string> out;
std::string cur;
for (char c : s) {
if (c == ',' || std::isspace(static_cast<unsigned char>(c))) {
if (!cur.empty()) out.push_back(cur);
cur.clear();
} else {
cur += c;
}
}
if (!cur.empty()) out.push_back(cur);
return out;
}
bool hasValidScheme(const std::string& url) {
static const std::regex kScheme(R"(^(udp|https?|wss?)://)", std::regex::icase);
return std::regex_search(url, kScheme);
}
} // namespace
std::vector<std::string> parseTrackers(const std::string& input) {
std::unordered_set<std::string> seen;
std::vector<std::string> out;
for (const auto& raw : splitOnCommaOrWhitespace(input)) {
const std::string url = trim(raw);
if (url.empty() || !hasValidScheme(url)) continue;
if (!seen.insert(url).second) continue;
out.push_back(url);
}
return out;
}
std::string formatTrackers(const std::vector<std::string>& trackers) {
std::string out;
for (std::size_t i = 0; i < trackers.size(); ++i) {
if (i) out += ", ";
out += trackers[i];
}
return out;
}
std::string trackersStatus(const std::vector<std::string>& saved, const std::string& fieldText) {
const auto next = parseTrackers(fieldText);
std::size_t tokenCount = 0;
for (const auto& t : splitOnCommaOrWhitespace(fieldText)) {
if (!trim(t).empty()) tokenCount++;
}
const std::size_t ignored = tokenCount - next.size();
const std::string savedLabel = saved.empty() ? "none saved" : std::to_string(saved.size()) + " saved";
const bool unchanged = ignored == 0 && next.size() == saved.size() && next == saved;
if (unchanged) {
return saved.empty() ? "none saved · comma or space separated"
: savedLabel + " · comma or space separated · empty clears";
}
if (tokenCount == 0) return savedLabel + " → empty clears all";
std::string line = savedLabel + " → will save " + std::to_string(next.size());
if (ignored > 0) line += " · " + std::to_string(ignored) + " ignored";
return line;
}
} // namespace torlinkc