foxygit / Torlinkc Log in
commits tags

/src/config/trackers.cpp · 2.42 KB

raw
#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