foxygit / Torlinkc Log in
commits tags

/src/engine/history.cpp · 2.32 KB

raw
#include "torlinkc/engine/history.hpp"

#include <algorithm>
#include <fstream>
#include <optional>
#include <sstream>

#include <nlohmann/json.hpp>

#include "torlinkc/config/paths.hpp"
#include "torlinkc/util/atomic_write.hpp"

using nlohmann::json;

namespace torlinkc {

namespace {

json toJson(const HistoryItem& h) {
  json j;
  j["id"] = h.id;
  j["name"] = h.name;
  if (!h.source.empty()) j["source"] = h.source;
  j["sizeBytes"] = h.sizeBytes;
  j["magnet"] = h.magnet;
  j["dir"] = h.dir;
  j["completedAt"] = h.completedAt;
  return j;
}

std::optional<HistoryItem> fromJson(const json& j) {
  if (!j.is_object()) return std::nullopt;
  auto idIt = j.find("id");
  auto nameIt = j.find("name");
  auto magnetIt = j.find("magnet");
  if (idIt == j.end() || !idIt->is_string()) return std::nullopt;
  if (nameIt == j.end() || !nameIt->is_string()) return std::nullopt;
  if (magnetIt == j.end() || !magnetIt->is_string()) return std::nullopt;

  HistoryItem h;
  h.id = idIt->get<std::string>();
  h.name = nameIt->get<std::string>();
  h.magnet = magnetIt->get<std::string>();
  if (auto v = j.find("source"); v != j.end() && v->is_string()) h.source = v->get<std::string>();
  if (auto v = j.find("dir"); v != j.end() && v->is_string()) h.dir = v->get<std::string>();
  if (auto v = j.find("sizeBytes"); v != j.end() && v->is_number()) h.sizeBytes = v->get<std::int64_t>();
  if (auto v = j.find("completedAt"); v != j.end() && v->is_number()) h.completedAt = v->get<std::int64_t>();
  return h;
}

}  // namespace

void saveHistory(const std::vector<HistoryItem>& items) {
  json arr = json::array();
  std::size_t n = std::min<std::size_t>(items.size(), kHistoryCap);
  for (std::size_t i = 0; i < n; ++i) arr.push_back(toJson(items[i]));
  try {
    writeJsonAtomic(paths::historyFile(), arr);
  } catch (...) {
  }
}

std::vector<HistoryItem> loadHistory() {
  std::vector<HistoryItem> out;
  std::ifstream in(paths::historyFile(), std::ios::binary);
  if (!in) return out;
  std::ostringstream ss;
  ss << in.rdbuf();
  json parsed;
  try {
    parsed = json::parse(ss.str());
  } catch (...) {
    return out;
  }
  if (!parsed.is_array()) return out;
  for (const auto& el : parsed) {
    if (out.size() >= static_cast<std::size_t>(kHistoryCap)) break;
    if (auto h = fromJson(el)) out.push_back(std::move(*h));
  }
  return out;
}

}  // namespace torlinkc