summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdwin van Leeuwen <edwinvanl@tuta.io>2022-09-23 19:03:05 +0000
committerEdwin van Leeuwen <edwinvanl@tuta.io>2022-09-23 19:03:05 +0000
commitc6288c5d21c37f5c86928f52a0ec3b364569d983 (patch)
tree726a3acb8e53a57969c4ef453addd1dd1101827f
parent78acb58d346e368c69c0ef49f1b25e29041736c0 (diff)
refactor: Started working on peek implementation
-rw-r--r--CMakeLists.txt7
-rw-r--r--src/rttt.hpp331
-rw-r--r--src/rttt/config.hpp2
-rw-r--r--test/catch_peek.cpp69
4 files changed, 242 insertions, 167 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7c76371..8cab78f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -113,10 +113,9 @@ set (SOURCES src/main.cpp)
# Setup executable
add_executable(${TARGET} ${SOURCES})
-target_include_directories(${TARGET} PRIVATE
- .
- src/
- test/include/
+target_include_directories(${TARGET}
+ PRIVATE src/
+ PRIVATE test/include/
)
target_link_libraries(${TARGET}
diff --git a/src/rttt.hpp b/src/rttt.hpp
index d1efc93..efdd20b 100644
--- a/src/rttt.hpp
+++ b/src/rttt.hpp
@@ -18,146 +18,143 @@
namespace rttt {
- // TODO: move to thing::type
- enum class SiteType : int {
- Unknown,
- HN,
- Reddit,
- RSS,
- Twitter,
- };
-
- // Consider moving this to view.hpp
- enum class list_mode : int {
- story,
- comment,
- feed,
- };
-
- // TODO: should work with constexpr, but might need newer g++/clang++
- inline std::vector<std::string> split(std::string_view strv,
- std::string_view delims = " ",
- size_t no_pieces = 0) {
- std::vector<std::string> output;
- size_t first = 0;
-
- while (first < strv.size()) {
- const auto second = strv.find_first_of(delims, first);
-
- if (first != second)
- output.emplace_back(strv.substr(first, second - first));
-
- if (second == std::string_view::npos) {
- break;
- } else if (no_pieces > 0 && output.size() >= no_pieces - 1) {
- output.emplace_back(strv.substr(second + 1, strv.size()));
- break;
- }
-
- first = second + 1;
+// TODO: move to thing::type
+enum class SiteType : int {
+ Unknown,
+ HN,
+ Reddit,
+ RSS,
+ Twitter,
+};
+
+// Consider moving this to view.hpp
+enum class list_mode : int {
+ story,
+ comment,
+ feed,
+};
+
+// TODO: should work with constexpr, but might need newer g++/clang++
+inline std::vector<std::string> split(std::string_view strv,
+ std::string_view delims = " ",
+ size_t no_pieces = 0) {
+ std::vector<std::string> output;
+ size_t first = 0;
+
+ while (first < strv.size()) {
+ const auto second = strv.find_first_of(delims, first);
+
+ if (first != second)
+ output.emplace_back(strv.substr(first, second - first));
+
+ if (second == std::string_view::npos) {
+ break;
+ } else if (no_pieces > 0 && output.size() >= no_pieces - 1) {
+ output.emplace_back(strv.substr(second + 1, strv.size()));
+ break;
}
- return output;
+ first = second + 1;
}
- inline void replaceAll(std::string & s, const std::string &search,
- const std::string &replace) {
- for (size_t pos = 0;; pos += replace.length()) {
- pos = s.find(search, pos);
- if (pos == std::string::npos)
- break;
- s.erase(pos, search.length());
- s.insert(pos, replace);
- }
+ return output;
+}
+
+inline void replaceAll(std::string &s, const std::string &search,
+ const std::string &replace) {
+ for (size_t pos = 0;; pos += replace.length()) {
+ pos = s.find(search, pos);
+ if (pos == std::string::npos)
+ break;
+ s.erase(pos, search.length());
+ s.insert(pos, replace);
}
-
- inline std::string parse_html(std::string str) {
- replaceAll(str, "<p>", "\n");
-
- std::string res;
- bool inTag = false;
- for (auto &ch : str) {
- if (ch == '<') {
- inTag = true;
- } else if (ch == '>') {
- inTag = false;
- } else {
- if (inTag == false) {
- res += ch;
- }
+}
+
+inline std::string parse_html(std::string str) {
+ replaceAll(str, "<p>", "\n");
+
+ std::string res;
+ bool inTag = false;
+ for (auto &ch : str) {
+ if (ch == '<') {
+ inTag = true;
+ } else if (ch == '>') {
+ inTag = false;
+ } else {
+ if (inTag == false) {
+ res += ch;
}
}
-
- replaceAll(res, "&#x2F;", "/");
- replaceAll(res, "&#x27;", "'");
- replaceAll(res, "&gt;", ">");
- replaceAll(res, "–", "-");
- replaceAll(res, "“", "\"");
- replaceAll(res, "”", "\"");
- replaceAll(res, "‘", "'");
- replaceAll(res, "’", "'");
- replaceAll(res, "„", "'");
- replaceAll(res, "&quot;", "\"");
- replaceAll(res, "&amp;", "&");
- replaceAll(res, "—", "-");
-
- return res;
}
- inline std::tm parse_time(std::string time_string) {
- std::tm time = {};
- std::regex e("(\\d{4})-(\\d+)-(\\d+)T(\\d+):(\\d+)");
- std::smatch sm;
- std::regex_search(time_string, sm, e);
- if (sm.size() > 1) {
- time.tm_year = std::stoi(sm[1]) - 1900;
- time.tm_mon = std::stoi(sm[2]) - 1;
- time.tm_mday = std::stoi(sm[3]);
- time.tm_hour = std::stoi(sm[4]);
- time.tm_min = std::stoi(sm[5]);
- }
- return time;
+ replaceAll(res, "&#x2F;", "/");
+ replaceAll(res, "&#x27;", "'");
+ replaceAll(res, "&gt;", ">");
+ replaceAll(res, "–", "-");
+ replaceAll(res, "“", "\"");
+ replaceAll(res, "”", "\"");
+ replaceAll(res, "‘", "'");
+ replaceAll(res, "’", "'");
+ replaceAll(res, "„", "'");
+ replaceAll(res, "&quot;", "\"");
+ replaceAll(res, "&amp;", "&");
+ replaceAll(res, "—", "-");
+
+ return res;
+}
+
+inline std::tm parse_time(std::string time_string) {
+ std::tm time = {};
+ std::regex e("(\\d{4})-(\\d+)-(\\d+)T(\\d+):(\\d+)");
+ std::smatch sm;
+ std::regex_search(time_string, sm, e);
+ if (sm.size() > 1) {
+ time.tm_year = std::stoi(sm[1]) - 1900;
+ time.tm_mon = std::stoi(sm[2]) - 1;
+ time.tm_mday = std::stoi(sm[3]);
+ time.tm_hour = std::stoi(sm[4]);
+ time.tm_min = std::stoi(sm[5]);
}
-
- // TODO: Move to text.hpp and rename
- inline std::vector<std::string> extractURL(std::string text) {
- std::vector<std::string> v;
-
- // Currently we assume an url always ends with alphanum, / or _
- // ([^/_[:alnum:]]) If this turns out to be false then we could also try to
- // filter out trailing punctuation marks etc specifically ([);:.!?]
- // std::regex e("(https?://[A-z0-9$–_.+!*‘(),./?=]+?)[),;:.!?]*(\\s|$)");
- // "(https?://[[:alnum:]$-_.+!*‘(),./?=;&#]+?)[^/_[:alnum:]]*(\\s|$)");
- std::regex e("(https?://[[:alnum:]$-_+!*‘,/?=;&#]+?)(\\]\\(|[^/"
- "_[:alnum:]]*(\\s|$))");
- std::smatch sm;
- while (std::regex_search(text, sm, e)) {
- if (sm.size() > 1)
- v.push_back(sm[1]);
- text = sm.suffix().str();
- }
-
- return v;
+ return time;
+}
+
+// TODO: Move to text.hpp and rename
+inline std::vector<std::string> extractURL(std::string text) {
+ std::vector<std::string> v;
+
+ // Currently we assume an url always ends with alphanum, / or _
+ // ([^/_[:alnum:]]) If this turns out to be false then we could also try to
+ // filter out trailing punctuation marks etc specifically ([);:.!?]
+ // std::regex e("(https?://[A-z0-9$–_.+!*‘(),./?=]+?)[),;:.!?]*(\\s|$)");
+ // "(https?://[[:alnum:]$-_.+!*‘(),./?=;&#]+?)[^/_[:alnum:]]*(\\s|$)");
+ std::regex e("(https?://[[:alnum:]$-_+!*‘,/?=;&#]+?)(\\]\\(|[^/"
+ "_[:alnum:]]*(\\s|$))");
+ std::smatch sm;
+ while (std::regex_search(text, sm, e)) {
+ if (sm.size() > 1)
+ v.push_back(sm[1]);
+ text = sm.suffix().str();
}
- struct Path {
- SiteType type = SiteType::Unknown;
- std::string name;
- std::string basename;
- list_mode mode = list_mode::story;
- std::string id;
- std::vector<std::string> parts;
- };
-
- inline Path parse_path(std::string_view path_name) {
- Path path;
- path.parts = split(path_name, "/");
- auto &v = path.parts;
- if (v[0] == "hn") {
- path.type = SiteType::HN;
- } else if (v[0] == "r") {
- path.type = SiteType::Reddit;
- } else if (v[0] == "rss") {
+ return v;
+}
+
+struct Path {
+ SiteType type = SiteType::Unknown;
+ std::string name;
+ std::string basename;
+ list_mode mode = list_mode::story;
+ std::string id;
+ std::vector<std::string> parts;
+};
+
+inline Path parse_path(std::string_view path_name) {
+ Path path;
+ path.parts = split(path_name, "/");
+ auto &v = path.parts;
+ if (v.size() == 1) {
+ if (v[0] == "rss") {
path.type = SiteType::RSS;
path.basename = "/rss";
if (v.size() == 2 && v[1] != "front") {
@@ -169,43 +166,53 @@ namespace rttt {
path.mode = list_mode::story;
}
return path;
- }
- if (v.size() == 4) {
- if (v[2] == "comments") {
- path.mode = list_mode::comment;
- } else {
- logger::log_ifnot(false);
- }
- path.name = "/" + v[0] + "/" + v[1] + "/" + v[2] + "/" + v[3];
- path.id = v[3];
- }
- path.basename = "/" + v[0] + "/" + v[1];
- if (path.name.empty())
+ } else {
+ path.basename = fmt::format("/{}", v[0]);
path.name = path.basename;
- return path;
+ return path;
+ }
}
-
- inline std::string timeSince(uint64_t t) {
- auto delta = current_time() - t;
- if (delta < 60)
- return std::to_string(delta) + " seconds";
- if (delta < 3600)
- return std::to_string(delta / 60) + " minutes";
- if (delta < 24 * 3600)
- return std::to_string(delta / 3600) + " hours";
- return std::to_string(delta / 24 / 3600) + " days";
+ if (v[0] == "hn") {
+ path.type = SiteType::HN;
+ } else if (v[0] == "r") {
+ path.type = SiteType::Reddit;
}
-
- inline int openInBrowser(std::string uri) {
- auto config = rttt::config::load();
- auto base_cmd = config["open_command"].get<std::string>();
+ if (v.size() == 4) {
+ if (v[2] == "comments") {
+ path.mode = list_mode::comment;
+ } else {
+ logger::log_ifnot(false);
+ }
+ path.name = "/" + v[0] + "/" + v[1] + "/" + v[2] + "/" + v[3];
+ path.id = v[3];
+ }
+ path.basename = "/" + v[0] + "/" + v[1];
+ if (path.name.empty())
+ path.name = path.basename;
+ return path;
+}
+
+inline std::string timeSince(uint64_t t) {
+ auto delta = current_time() - t;
+ if (delta < 60)
+ return std::to_string(delta) + " seconds";
+ if (delta < 3600)
+ return std::to_string(delta / 60) + " minutes";
+ if (delta < 24 * 3600)
+ return std::to_string(delta / 3600) + " hours";
+ return std::to_string(delta / 24 / 3600) + " days";
+}
+
+inline int openInBrowser(std::string uri) {
+ auto config = rttt::config::load();
+ auto base_cmd = config["open_command"].get<std::string>();
#ifdef __APPLE__
- if (base_command == "xdg-open")
- base_cmd = "open";
+ if (base_command == "xdg-open")
+ base_cmd = "open";
#endif
- // std::string cmd = "run-mailcap " + uri + " > /dev/null 2>&1";
- std::string cmd = base_cmd + " \"" + uri + "\" > /dev/null 2>&1 &";
- logger::push("EXECUTING: {}", cmd);
- return system(cmd.c_str());
- }
+ // std::string cmd = "run-mailcap " + uri + " > /dev/null 2>&1";
+ std::string cmd = base_cmd + " \"" + uri + "\" > /dev/null 2>&1 &";
+ logger::push("EXECUTING: {}", cmd);
+ return system(cmd.c_str());
+}
} // namespace rttt
diff --git a/src/rttt/config.hpp b/src/rttt/config.hpp
index ed8ef35..360ca99 100644
--- a/src/rttt/config.hpp
+++ b/src/rttt/config.hpp
@@ -8,7 +8,7 @@
#include "nlohmann/json.hpp"
#include "functional.hpp"
-#include "rttt/prompt.hpp"
+#include "prompt.hpp"
namespace rttt {
diff --git a/test/catch_peek.cpp b/test/catch_peek.cpp
new file mode 100644
index 0000000..2397212
--- /dev/null
+++ b/test/catch_peek.cpp
@@ -0,0 +1,69 @@
+#define CATCH_CONFIG_MAIN
+
+#include "catch2/catch.hpp"
+
+#include "rttt.hpp"
+#include "rttt/thing.hpp"
+
+namespace rttt {
+namespace peek {
+
+struct peek_state {
+ std::vector<std::string> paths = {"/r/front", "/rss/Mike Blumenkrantz",
+ "/hn/top", "/r/funny", "/tw/awesomekling"};
+ thing::state *thing_state;
+};
+
+using state = std::shared_ptr<peek_state>;
+
+bool is_valid_path(std::string_view path) {
+ return path == "/peek";
+}
+
+bool is_valid_path(const rttt::Path &path) {
+ return path.basename == "/peek";
+}
+
+std::optional<rttt::Path> parse_path(std::string_view path) {
+ if (peek::is_valid_path(path)) {
+ return rttt::parse_path(path);
+ }
+ return std::nullopt;
+}
+
+auto setup(state state, nlohmann::json config, thing::state * tstate) {
+ if (!state)
+ state = std::make_shared<peek_state>();
+ state->thing_state = tstate;
+ if (config.contains("peek") && config["peek"].contains("paths")) {
+ state->paths = config["peek"]["paths"].get<std::vector<std::string>>();
+ }
+ return state;
+}
+} // namespace peek
+} // namespace rttt
+
+using namespace rttt;
+
+// TODO: should we add a thing::is_valid_path(state, path), which uses the state to
+// infer which type to test for?
+//
+SCENARIO("We can parse peek path") {
+ REQUIRE(peek::is_valid_path("/peek"));
+ REQUIRE(!peek::is_valid_path("/r/peek"));
+
+ auto pth = peek::parse_path("/peek");
+ REQUIRE(pth.has_value());
+ REQUIRE(pth.value().parts[0] == "peek");
+ REQUIRE(peek::is_valid_path(pth.value()));
+
+ pth = peek::parse_path("/r/peek");
+ REQUIRE(!pth.has_value());
+}
+
+SCENARIO("We can use peek state") {
+ thing::state thing_state;
+ peek::state state;
+ nlohmann::json config;
+ peek::setup(state, config, &thing_state);
+}