mmcs-quotes-bridge/state.cpp

142 lines
4.8 KiB
C++

#include "state.h"
#include "spdlog/spdlog.h"
#include <exception>
#include <fstream>
#include <nlohmann/json.hpp>
#include <sstream>
#include <string>
using namespace state;
using namespace nlohmann;
AppState::AppState(std::string filename, config::AppConfig *cfg) : m_saveFilename(filename), m_config(cfg) {
for (int i = 0; i < cfg->vkSources.size(); ++i) {
vkRepostState.emplace_back();
}
for (int i = 0; i < cfg->tgSources.size(); ++i) {
tgRepostState.emplace_back();
}
std::ifstream f(filename);
if (f.fail()) {
spdlog::error("no state file");
return;
}
json state = json::parse(f);
if (state.type() != json::value_t::object) {
throw InvalidSavedStateException("JSON root must be an object");
}
if (state.contains("vk")) {
json vkStates_ = state["vk"];
if (vkStates_.type() == json::value_t::object) {
for (auto& [key, vkState_] : vkStates_.items()) {
if (key.empty()) continue;
int sourceIndex;
RepostState &&s {};
if (key[0] >= '0' && key[0] <= '9') {
long keyNum;
try {
keyNum = std::stol(key);
} catch (std::exception &e) {
throw InvalidSavedStateException("invalid number in vk source id");
}
sourceIndex = std::distance(
m_config->vkSources.begin(),
std::find_if(m_config->vkSources.begin(), m_config->vkSources.end(),
[=](auto &src){ return src.id.index() == 0 && std::get<long>(src.id) == std::stoi(key); }
)
);
} else {
sourceIndex = std::distance(
m_config->vkSources.begin(),
std::find_if(m_config->vkSources.begin(), m_config->vkSources.end(),
[=](auto &src){ return src.id.index() == 1 && std::get<std::string>(src.id) == key; }
)
);
}
if (vkState_.contains("last_post_date")) {
json lastPostDate_ = vkState_["last_post_date"];
if (lastPostDate_.type() == json::value_t::number_integer ||
lastPostDate_.type() == json::value_t::number_unsigned) {
s.lastForwardedPostDate = vkState_["last_post_date"];
} else {
throw InvalidSavedStateException("key vk.last_post_date must be an integer");
}
}
if (sourceIndex < m_config->vkSources.size())
vkRepostState[sourceIndex] = s;
}
} else {
throw InvalidSavedStateException("key vk must be an object");
}
}
if (state.contains("tg")) {
json tgStates_ = state["tg"];
if (tgStates_.type() == json::value_t::object) {
for (auto& [key, tgState_] : tgStates_.items()) {
if (key.empty()) continue;
int sourceIndex;
RepostState &&s {};
long keyNum;
try {
keyNum = std::stol(key);
} catch (std::exception &e) {
throw InvalidSavedStateException("invalid number in tg source id");
}
sourceIndex = std::distance(
m_config->tgSources.begin(),
std::find_if(m_config->tgSources.begin(), m_config->tgSources.end(),
[=](auto &src){ return src.id == keyNum; }
)
);
if (tgState_.contains("last_post_date")) {
json lastPostDate_ = tgState_["last_post_date"];
if (lastPostDate_.type() == json::value_t::number_integer ||
lastPostDate_.type() == json::value_t::number_unsigned) {
s.lastForwardedPostDate = tgState_["last_post_date"];
} else {
throw InvalidSavedStateException("key tg.last_post_date must be an integer");
}
}
if (sourceIndex < m_config->tgSources.size())
tgRepostState[sourceIndex] = s;
}
} else {
throw InvalidSavedStateException("key tg must be an object");
}
}
}
void AppState::save() {
if (m_saveFilename.empty()) return;
spdlog::info("saving state");
json state = {{"vk", json::object()}, {"tg", json::object()}};
for (int i = 0; i < m_config->vkSources.size(); ++i) {
auto &s = vkRepostState[i];
json st = {{"last_post_date", s.lastForwardedPostDate}};
auto &vkId = m_config->vkSources[i].id;
std::string vkIdStr;
if (vkId.index() == 0) vkIdStr = std::to_string(std::get<long>(vkId));
else vkIdStr = std::get<std::string>(vkId);
state["vk"].emplace(vkIdStr, st);
}
for (int i = 0; i < m_config->tgSources.size(); ++i) {
auto &s = tgRepostState[i];
json st = {{"last_post_date", s.lastForwardedPostDate}};
state["tg"].emplace(std::to_string(m_config->tgSources[i].id), st);
}
std::ofstream f(m_saveFilename.c_str());
if (f.fail()) {
spdlog::error("failed to open state file '{}'", m_saveFilename);
return;
}
f << state;
}
std::string AppState::to_string() {
std::stringstream output;
output << "<not implemented>";
return output.str();
}