Command done

This commit is contained in:
Love 2025-01-24 12:35:54 +01:00
parent 28613af006
commit 40d98cdbb6
8 changed files with 195 additions and 6 deletions

View File

@ -7,6 +7,10 @@ add_executable(${PROJECT_NAME}
Page.cpp Page.cpp
stringutil.hpp stringutil.hpp
stringutil.cpp stringutil.cpp
Tui.cpp
Tui.h
Command.h
Command.cpp
) )
target_link_libraries(${PROJECT_NAME} PRIVATE LibXml2::LibXml2 cpr::cpr) target_link_libraries(${PROJECT_NAME} PRIVATE LibXml2::LibXml2 cpr::cpr)

51
src/Command.cpp Normal file
View File

@ -0,0 +1,51 @@
#include "Command.h"
#include <iostream>
namespace Command {
Command readCommand() {
if (!std::cin)
return {Exit{}};
std::string line;
if (!std::getline(std::cin, line))
return {Exit{}};
// Trim leading and trailing whitespaces
constexpr std::string_view TO_TRIM = " \t\n\r";
const size_t start = line.find_first_not_of(TO_TRIM);
const size_t end = line.find_last_not_of(TO_TRIM);
if (start == std::string::npos) return None{};
const std::string_view line_trimmed(line.data() + start, end - start + 1);
using namespace std::string_view_literals;
if (line_trimmed == "r"sv || line_trimmed == "refresh"sv)
return Refresh{};
if (line_trimmed == "n"sv || line_trimmed == ">"sv || line_trimmed == "next"sv)
return Next{};
if (line_trimmed == "p"sv || line_trimmed == "<"sv || line_trimmed == "previous"sv)
return Previous{};
if (line_trimmed == "x"sv || line_trimmed == "exit"sv || line_trimmed == "q"sv || line_trimmed == "quit"sv)
return Exit{};
if (line_trimmed == "help"sv || line_trimmed == "?"sv || line_trimmed == "?"sv)
return Help{};
if (line_trimmed.starts_with("seek") || line.starts_with("s ")) {
const size_t space = line_trimmed.find(' ');
if (space == std::string::npos)
throw NoNumberException();
int number = 0;
const size_t size = line.size();
for (size_t i = space + 1; i < size; ++i) {
const char c = line_trimmed[i];
if (!std::isdigit(c))
throw NoNumberException();
number = number * 10 + (c - '0');
}
return {Seek{number}};
}
return {None{}};
}
}

41
src/Command.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include <string>
#include <variant>
namespace Command {
class NoNumberException final : public std::exception {
public:
[[nodiscard]] const char *what() const noexcept override {
return "Seek option was used without a number";
}
};
struct Help {
};
struct None {
};
struct Next {
};
struct Previous {
};
struct Refresh {
};
struct Exit {
};
struct Seek {
int number;
};
using Command = std::variant<None, Help, Next, Previous, Refresh, Exit, Seek>;
Command readCommand();
}

View File

@ -74,7 +74,7 @@ Page &Page::operator-=(int) {
return *this; return *this;
} }
std::string Page::str() const { std::string Page::str_pretty() const {
std::string ret; std::string ret;
std::istringstream stream(m_subpage); std::istringstream stream(m_subpage);

View File

@ -32,7 +32,7 @@ public:
Page &operator-=(int); Page &operator-=(int);
[[nodiscard]] std::string str() const; [[nodiscard]] std::string str_pretty() const;
bool refresh(); bool refresh();

81
src/Tui.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "Tui.h"
#include <iostream>
#include <optional>
#include <ostream>
#include <utility>
#include "Command.h"
Tui::Tui(Page page) : m_page(std::move(page)) {
}
std::optional<int> get_page_number(const std::string_view line) {
const size_t space = line.find_first_of(' ');
if (space == std::string::npos)
return std::nullopt;
int number = 0;
const size_t size = line.size();
for (size_t i = space; i < size; i++) {
const char c = line[i];
if (!std::isdigit(c))
return std::nullopt;
number = number * 10 + (c - '0');
}
return number;
}
void printHelp() {
std::cout
<< "Available Commands:\n"
<< " r, refresh : Refresh the current view.\n"
<< " n, >, next : Move to the next item.\n"
<< " p, <, previous : Move to the previous item.\n"
<< " x, exit, q, quit : Exit the application.\n"
<< " seek <number>, s <number> : Seek to the specified number.\n"
<< " help, h, ? : Display this help message."
<< std::endl;
}
void Tui::run() {
std::cout << m_page.str_pretty() << std::endl;
for (;;) {
std::cout << "Enter a command: ";
std::flush(std::cout);
const Command::Command command = Command::readCommand();
bool should_exit = false;
auto visitor = [this, &should_exit]<typename T0>(T0 &&arg) {
using T = std::decay_t<T0>;
if constexpr (std::is_same_v<T, Command::None>) {
std::cout << "Invalid command!" << std::endl;
} else if constexpr (std::is_same_v<T, Command::Next>) {
m_page += 1;
std::cout << m_page.str_pretty() << std::endl;
} else if constexpr (std::is_same_v<T, Command::Previous>) {
m_page -= 1;
std::cout << m_page.str_pretty() << std::endl;
} else if constexpr (std::is_same_v<T, Command::Refresh>) {
m_page.refresh();
std::cout << m_page.str_pretty() << std::endl;
} else if constexpr (std::is_same_v<T, Command::Exit>) {
should_exit = true;
} else if constexpr (std::is_same_v<T, Command::Help>) {
printHelp();
} else if constexpr (std::is_same_v<T, Command::Seek>) {
const Command::Seek &seek = arg;
const int number = seek.number;
m_page = Page(number);
std::cout << m_page.str_pretty() << std::endl;
}
};
std::visit(visitor, command);
if (should_exit)
return;
}
}

13
src/Tui.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "Page.hpp"
class Tui {
public:
Tui(Page page);
void run();
private:
Page m_page;
};

View File

@ -1,8 +1,7 @@
#include <iostream>
#include "Page.hpp" #include "Page.hpp"
#include "Tui.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
const auto page = Page(); Tui tui = Page();
std::cout << page.str() << std::endl; tui.run();
} }