diff --git a/CMakeLists.txt b/CMakeLists.txt index 642f02f..906a0ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ add_subdirectory(libuv) add_subdirectory(spdlog) add_subdirectory(td) -add_executable(${PROJECT_NAME} main.cpp http.cpp vk.cpp) +add_executable(${PROJECT_NAME} main.cpp http.cpp state.cpp vk.cpp) target_compile_options(${PROJECT_NAME} PRIVATE -std=c++2b) diff --git a/main.cpp b/main.cpp index 0aabf45..a05653e 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,6 @@ -#include "http.h" +#include "spdlog/spdlog.h" +#include "state.h" +#include #include #include #include @@ -20,7 +22,17 @@ int main() { uv_signal_t signalHandles[2] = {}; create_signal_handles(loop, signalHandles); + state::AppState state; + try { + state = state::AppState("bridge_state.json"); + spdlog::info("state: {}", state.to_string()); + } catch (state::InvalidSavedStateException &e) { + spdlog::error("invalid saved state: {}", e.message); + return 1; + } + uv_run(loop, UV_RUN_DEFAULT); spdlog::info("event loop ended"); + state.save(); return 0; } \ No newline at end of file diff --git a/state.cpp b/state.cpp new file mode 100644 index 0000000..1e7db4a --- /dev/null +++ b/state.cpp @@ -0,0 +1,67 @@ +#include "state.h" +#include "spdlog/spdlog.h" +#include +#include +#include + +using namespace state; +using namespace nlohmann; + +AppState::AppState(std::string filename) : m_saveFilename(filename) { + std::ifstream f(filename); + if (f.fail()) { + spdlog::info("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 vkState = state["vk"]; + if (vkState.type() == json::value_t::object) { + if (vkState.contains("last_post_id")) { + json lastPostId = vkState["last_post_id"]; + if (lastPostId.type() == json::value_t::number_integer || lastPostId.type() == json::value_t::number_unsigned) { + vkLastPostId = lastPostId; + } else { + throw InvalidSavedStateException("key vk.last_post_id must be an integer"); + } + } + } else { + throw InvalidSavedStateException("key vk must be an object"); + } + } + + if (state.contains("tg")) { + json tgState = state["tg"]; + if (tgState.type() == json::value_t::object) { + if (tgState.contains("last_post_id")) { + json lastPostId = tgState["last_post_id"]; + if (lastPostId.type() == json::value_t::number_integer || lastPostId.type() == json::value_t::number_unsigned) { + tgLastPostId = lastPostId; + } else { + throw InvalidSavedStateException("key tg.last_post_id must be an integer"); + } + } + } else { + throw InvalidSavedStateException("key tg must be an object"); + } + } +} + +void AppState::save() { + if (m_saveFilename.empty()) return; + spdlog::info("saving state"); + json state = {{"vk", {{"last_post_id", vkLastPostId}}}, {"tg", {{"last_post_id", tgLastPostId}}}}; + std::ofstream f(m_saveFilename.c_str()); + if (f.fail()) { + spdlog::error("failed to open state file '{}'", m_saveFilename); + return; + } + f << state << std::endl; +} + +std::string AppState::to_string() { + return std::format("AppState {{ tgLastPostId: {}, vkLastPostId: {} }}", tgLastPostId, vkLastPostId); +} \ No newline at end of file diff --git a/state.h b/state.h new file mode 100644 index 0000000..a03b212 --- /dev/null +++ b/state.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace state { + class InvalidSavedStateException : std::exception { + public: + inline InvalidSavedStateException(const char *message): message(message) {}; + const char *message; + }; + + class AppState { + public: + inline AppState() {}; + AppState(std::string filename); + std::string to_string(); + void save(); + + int64_t tgLastPostId = 0; + int64_t vkLastPostId = 0; + private: + std::string m_saveFilename; + }; +} \ No newline at end of file