summaryrefslogtreecommitdiffstats
path: root/src/rttt/request.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rttt/request.hpp')
-rw-r--r--src/rttt/request.hpp103
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