Compare commits
	
		
			2 Commits
		
	
	
		
			2295fdc3ce
			...
			c09929d2f7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c09929d2f7 | |||
| 360a85b7bf | 
							
								
								
									
										88
									
								
								src/Page.cpp
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								src/Page.cpp
									
									
									
									
									
								
							| @@ -10,26 +10,67 @@ | |||||||
|  |  | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <format> | #include <format> | ||||||
|  | #include <algorithm> | ||||||
|  |  | ||||||
|  | #include "ansi.hpp" | ||||||
| #include "stringutil.hpp" | #include "stringutil.hpp" | ||||||
| #include "cpr/cpr.h" | #include "cpr/cpr.h" | ||||||
| #include "libxml/HTMLparser.h" | #include "libxml/HTMLparser.h" | ||||||
| #include "libxml/xpath.h" | #include "libxml/xpath.h" | ||||||
|  |  | ||||||
|  |  | ||||||
| Page::Page(const uint_fast8_t number): number(number), subpages(fetchSubpages()) { | bool is_number(std::string_view s){ | ||||||
|  |     return std::all_of(s.begin(), s.end(), [](unsigned char c ){ | ||||||
|  |         return std::isdigit(c); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void italize_numbers(std::string &content, size_t leave_chars = 0) { | ||||||
|  |     size_t end = content.size(); | ||||||
|  |      | ||||||
|  |     // Process backwards, word by word | ||||||
|  |     for (;;) { | ||||||
|  |         size_t space = content.rfind(' ', end - 1); | ||||||
|  |         size_t begin = (space == std::string::npos) ? 0 : space + 1; | ||||||
|  |         size_t word_length = end - begin; | ||||||
|  |          | ||||||
|  |         if (is_number(content.substr(begin, word_length))) { | ||||||
|  |             content.insert(end, ansi::CLEAR); | ||||||
|  |             content.insert(begin, ansi::ITALIC); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (space == std::string::npos) | ||||||
|  |             break; | ||||||
|  |         if (leave_chars >= space) | ||||||
|  |             break; | ||||||
|  |          | ||||||
|  |         end = space; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void pretty_format_page(std::string &content) { | ||||||
|  |     content.insert(0, ansi::BOLD); | ||||||
|  |     size_t line_end = content.find("\n"); | ||||||
|  |     content.insert(line_end, ansi::CLEAR); | ||||||
|  |  | ||||||
|  |     italize_numbers(content, line_end + ansi::CLEAR.size()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Page::Page(const uint_fast8_t number): m_number(number), m_subpage(fetchSubpage()) { | ||||||
| } | } | ||||||
|  |  | ||||||
| Page Page::operator--(int) const { | Page Page::operator--(int) const { | ||||||
|     return Page(number - 1); |     return Page(m_number - 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| Page Page::operator++(int) const { | Page Page::operator++(int) const { | ||||||
|     return Page(number + 1); |     return Page(m_number + 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| Page &Page::operator-=(int) { | Page &Page::operator-=(int) { | ||||||
|     number--; |     m_number--; | ||||||
|     refresh(); |     refresh(); | ||||||
|     return *this; |     return *this; | ||||||
| } | } | ||||||
| @@ -37,38 +78,38 @@ Page &Page::operator-=(int) { | |||||||
| std::string Page::str() const { | std::string Page::str() const { | ||||||
|     std::string ret; |     std::string ret; | ||||||
|  |  | ||||||
|     for (const std::string &pageText: subpages) { |     std::istringstream stream(m_subpage); | ||||||
|         std::istringstream stream(pageText); |  | ||||||
|     std::string line; |     std::string line; | ||||||
|     while (std::getline(stream, line)) |     while (std::getline(stream, line)) | ||||||
|         ret += line + "\n"; |         ret += line + "\n"; | ||||||
|  |  | ||||||
|     string_utils::limitConsecutiveWhitespace(ret, MAX_WHITESPACE); |     string_utils::limitConsecutiveWhitespace(ret, MAX_WHITESPACE); | ||||||
|     string_utils::removeTrailingWhitespace(ret); |     string_utils::removeTrailingWhitespace(ret); | ||||||
|     } |  | ||||||
|  |     pretty_format_page(ret); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| Page &Page::operator+=(int) { | Page &Page::operator+=(int) { | ||||||
|     number++; |     m_number++; | ||||||
|     refresh(); |     refresh(); | ||||||
|     return *this; |     return *this; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool Page::refresh() { | bool Page::refresh() { | ||||||
|     std::vector<std::string> newSubpages = fetchSubpages(); |     std::string newSubpage = fetchSubpage(); | ||||||
|     const bool replace = !contentEquals(newSubpages); |     const bool replace = newSubpage != m_subpage; | ||||||
|     if (replace) |     if (replace) | ||||||
|         subpages = newSubpages; |         m_subpage = newSubpage; | ||||||
|  |  | ||||||
|     return replace; |     return replace; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::string Page::url() const { | std::string Page::url() const { | ||||||
|     return std::format("https://www.svt.se/svttext/web/pages/{}.html", number); |     return std::format("https://www.svt.se/svttext/web/pages/{}.html", m_number); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::vector<std::string> Page::fetchSubpages() const { | std::string Page::fetchSubpage() const { | ||||||
|     const cpr::Response response = cpr::Get(cpr::Url{url()}); |     const cpr::Response response = cpr::Get(cpr::Url{url()}); | ||||||
|     if (response.status_code / 100 != 2) |     if (response.status_code / 100 != 2) | ||||||
|         throw std::runtime_error("Page not found"); |         throw std::runtime_error("Page not found"); | ||||||
| @@ -91,14 +132,16 @@ std::vector<std::string> Page::fetchSubpages() const { | |||||||
|         throw std::runtime_error("Could not evaluate XPath expression."); |         throw std::runtime_error("Could not evaluate XPath expression."); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::vector<std::string> pages; |     // There's only one valid page | ||||||
|  |     std::string page; | ||||||
|     if (const xmlNodeSetPtr nodes = xpathObj->nodesetval) { |     if (const xmlNodeSetPtr nodes = xpathObj->nodesetval) { | ||||||
|         for (int i = 0; i < nodes->nodeNr; ++i) { |         for (int i = 0; i < nodes->nodeNr; i++) { | ||||||
|             xmlChar *content = xmlNodeGetContent(nodes->nodeTab[i]); |             xmlChar *content = xmlNodeGetContent(nodes->nodeTab[i]); | ||||||
|             if (!content) |             if (!content) | ||||||
|                 continue; |                 continue; | ||||||
|             pages.emplace_back(reinterpret_cast<const char *>(content)); |             page = reinterpret_cast<const char *>(content); | ||||||
|             xmlFree(content); |             xmlFree(content); | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -106,16 +149,5 @@ std::vector<std::string> Page::fetchSubpages() const { | |||||||
|     xmlXPathFreeContext(xpathCtx); |     xmlXPathFreeContext(xpathCtx); | ||||||
|     xmlFreeDoc(doc); |     xmlFreeDoc(doc); | ||||||
|  |  | ||||||
|     return pages; |     return page; | ||||||
| } |  | ||||||
|  |  | ||||||
| bool Page::contentEquals(const std::vector<std::string> &subpagesOther) const { |  | ||||||
|     if (subpagesOther.size() != subpages.size()) |  | ||||||
|         return false; |  | ||||||
|  |  | ||||||
|     for (std::size_t i = 0; i < subpages.size(); i++) |  | ||||||
|         if (subpages[i] != subpagesOther[i]) |  | ||||||
|             return false; |  | ||||||
|  |  | ||||||
|     return true; |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,8 +18,8 @@ static constexpr uint_fast8_t MAX_WHITESPACE = 2; | |||||||
| class Page | class Page | ||||||
| { | { | ||||||
| private: | private: | ||||||
|     uint_fast8_t number{}; |     uint_fast8_t m_number{}; | ||||||
|     std::vector<std::string> subpages; |     std::string m_subpage; | ||||||
|  |  | ||||||
| public: | public: | ||||||
|     explicit Page(uint_fast8_t number = DEFAULT_NUMBER); |     explicit Page(uint_fast8_t number = DEFAULT_NUMBER); | ||||||
| @@ -39,7 +39,7 @@ public: | |||||||
| private: | private: | ||||||
|     [[nodiscard]] std::string url() const; |     [[nodiscard]] std::string url() const; | ||||||
|  |  | ||||||
|     [[nodiscard]] std::vector<std::string> fetchSubpages() const; |     [[nodiscard]] std::string fetchSubpage() const; | ||||||
|  |  | ||||||
|     [[nodiscard]] bool contentEquals(const std::vector<std::string> &subpagesOther) const; |     [[nodiscard]] bool contentEquals(const std::vector<std::string> &subpagesOther) const; | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								src/ansi.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/ansi.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  |  | ||||||
|  | namespace ansi | ||||||
|  | { | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | constexpr std::string_view BOLD = "\033[1m"; | ||||||
|  | constexpr std::string_view ITALIC = "\033[3m"; | ||||||
|  | constexpr std::string_view CLEAR = "\033[0m"; | ||||||
|  |  | ||||||
|  | } // namespace ansi | ||||||
		Reference in New Issue
	
	Block a user