diff options
Diffstat (limited to 'src/rttt/request.hpp')
-rw-r--r-- | src/rttt/request.hpp | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/src/rttt/request.hpp b/src/rttt/request.hpp new file mode 100644 index 0000000..a6aef51 --- /dev/null +++ b/src/rttt/request.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include <future> +#include <optional> +#include <queue> +#include <string> +#include <unordered_map> + +#include "cpr/cpr.h" + +#include "rttt/logger.hpp" + +namespace rttt { +namespace request { + +template <typename T> struct State { + std::queue<std::pair<T, std::string>> requestQueue; + std::unordered_map<T, std::string> idUrlMap; + std::unordered_map<T, std::future<cpr::Response>> requestMap; + std::unordered_map<T, std::string> requestResults; +}; + +/** + * Push url to the request queue using a custom id + */ +template <typename T> +State<T> push(State<T> &&state, const std::string &uri, const T &id) { + state.requestQueue.push({id, uri}); + state.idUrlMap[id] = uri; + return std::move(state); +} + +template <typename T, typename F, typename H> +State<T> update(State<T> &&state, F &request_function, H &error_handling) { + while (!state.requestQueue.empty()) { + const auto &pair = state.requestQueue.front(); + + logger::push("URI requested: {}", pair.second); + + state.requestMap[pair.first] = request_function(pair.second); + + state.requestQueue.pop(); + } + + for (auto &pair : state.requestMap) { + if (pair.second.valid()) { + auto status = pair.second.wait_for(std::chrono::seconds(0)); + if (status == std::future_status::ready) { + auto r = pair.second.get(); + if (r.error.code != cpr::ErrorCode::OK || r.status_code != 200) { + logger::push("ERROR: cpr error code {}", r.error.message); + logger::push("ERROR: {} status_code {}", pair.first, r.status_code); + // FIXME: Pass r.error.code? + if (error_handling(r.status_code)) { + logger::push("URI requested after error: {}", state.idUrlMap[pair.first]); + state.requestQueue.push({pair.first, state.idUrlMap[pair.first]}); + } + continue; + } + state.requestResults[pair.first] = r.text; + } + } + } + // Remove completed futures from the futuresMap + std::erase_if(state.requestMap, + [](auto &pair) { return !pair.second.valid(); }); + + return std::move(state); +} + +template <typename T> State<T> update(State<T> &&state) { + auto request_function = [](const std::string &uri) { + return cpr::GetAsync(cpr::Url{uri}, cpr::Timeout{5000}); + }; + auto error_handling = [](int) { return false; }; + return update(std::move(state), request_function, error_handling); +} + +template <typename T> +std::optional<std::string> try_retrieve(State<T> &state, const T &id) { + if (state.requestResults.contains(id)) { + auto data = std::move(state.requestResults[id]); + state.requestResults.erase(id); + state.idUrlMap.erase(id); + return data; + } + return std::nullopt; +} + +template <typename T> +std::optional<std::pair<T, std::string>> try_pop_and_retrieve(State<T> &state) { + if (state.requestResults.empty()) + return std::nullopt; + + auto it = state.requestResults.begin(); + std::optional<std::pair<T, std::string>> result = *it; + state.idUrlMap.erase(it->first); + + state.requestResults.erase(it); + return result; +} +} // namespace request +} // namespace rttt |