stamen

Stamen - static menu generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE

commit 199eba24714cb83271e636100315011def07ded9
parent 8fe2a8b55ac5c587ad03da22c3e5cc080b7e5687
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Fri, 14 Jun 2024 11:13:49 +0200

General code improvement

Diffstat:
Mdemo/demo_menu.conf | 33++++++++++++++++++---------------
Minclude/menu.h | 64+++++++++++++++++++++++++++-------------------------------------
Msrc/menu.cpp | 52+++++++++++++++++++++++++++++-----------------------
Msrc/stamen.cpp | 20++++++++------------
4 files changed, 82 insertions(+), 87 deletions(-)

diff --git a/demo/demo_menu.conf b/demo/demo_menu.conf @@ -1,25 +1,28 @@ -+ menu_main Main Menu ++ menu_main Main Menu -- submenu_1 Enter Submenu 1 -- submenu_2 Enter Submenu 2 +- submenu_1 Enter Submenu 1 +- submenu_2 Enter Submenu 2 - finish Quit + + submenu_1 Submenu 1 -- submenu_3 Enter Submenu 3 -- operation1 Operation 1 -- operation2 Operation 2 -- operation3 Operation 3 +- submenu_3 Enter Submenu 3 +- operation1 Operation 1 +- operation2 Operation 2 +- operation3 Operation 3 + + submenu_2 Submenu 2 -- submenu_3 Enter Submenu 3 -- operation1 Operation 1 -- operation2 Operation 2 -- operation3 Operation 3 +- submenu_3 Enter Submenu 3 +- operation1 Operation 1 +- operation2 Operation 2 +- operation3 Operation 3 + -+ submenu_3 Submenu 3 ++ submenu_3 Submenu 3 -- operation1 Operation 1 -- operation2 Operation 2 -- operation3 Operation 3 +- operation1 Operation 1 +- operation2 Operation 2 +- operation3 Operation 3 diff --git a/include/menu.h b/include/menu.h @@ -21,6 +21,9 @@ class Menu { Menu(const Menu &) = delete; Menu &operator=(const Menu &) = delete; + Menu(Menu &&) = delete; + Menu &operator=(Menu &&) = delete; + ~Menu() noexcept = default; static int dynamic(const std::string &code, display_f display) { Menu::display_stub_default = code; @@ -29,30 +32,19 @@ class Menu { }; static void read(const std::string &s); - static void print(const std::string &entry) { print(entry, 1); } static void insert(const std::string &s, callback_f callback) { free_lookup.emplace(s, callback); } - [[nodiscard]] const std::string &getCode() const { return code; } - [[nodiscard]] const std::string &getTitle() const { return title; } + const std::string &getCode() const { return code; } + const std::string &getTitle() const { return title; } - [[nodiscard]] const item_t *getItemv() const { - return entries.items.data(); - } - [[nodiscard]] std::size_t getSize() const { return entries.items.size(); } - - [[nodiscard]] const std::string &getCode(std::size_t idx) const { - return entries.codes[idx].code; - } + const item_t *getItemv() const { return entries.items.data(); } + std::size_t getSize() const { return entries.items.size(); } - [[nodiscard]] const std::string &getPrompt(std::size_t idx) const { - return entries.codes[idx].prompt; - } - - [[nodiscard]] callback_f getCallback(std::size_t idx) const { - return entries.items[idx].callback; - } + callback_f getCallback(std::size_t idx) const; + const std::string &getCode(std::size_t idx) const; + const std::string &getPrompt(std::size_t idx) const; static std::unordered_map<std::string, Menu> menu_lookup; @@ -60,36 +52,34 @@ class Menu { Menu(std::string code, std::string prompt) : code(std::move(code)), title(std::move(prompt)) {} - static void print(const std::string &entry, const int depth); - static int display_stub(int idx); + struct entries_t { + entries_t() = default; + entries_t(const entries_t &) = delete; + entries_t &operator=(const entries_t &) = delete; + entries_t(entries_t &&) = delete; + entries_t &operator=(entries_t &&) = delete; + ~entries_t() noexcept { + for (const auto [_, prompt] : items) { + delete[] prompt; + } + } + + void insert(const std::string &code, const std::string &prompt, + callback_f callback = display_stub); - struct Entries { struct code_t { std::string code; std::string prompt; }; - ~Entries() { - for (const auto [_, prompt] : items) { - delete[] prompt; - } - } - std::vector<code_t> codes; std::vector<item_t> items; - - void insert(const std::string &code, const std::string &prompt, - callback_f callback = display_stub) { - char *buffer = new char[prompt.size() + 1]; - strcpy(buffer, prompt.c_str()); - items.emplace_back(callback, buffer); - - codes.emplace_back(code, prompt); - } }; const std::string code, title; - Entries entries; + entries_t entries; + + static int display_stub(int idx); static std::unordered_map<std::string, callback_f> free_lookup; static std::string display_stub_default; diff --git a/src/menu.cpp b/src/menu.cpp @@ -15,48 +15,54 @@ std::unordered_map<std::string, callback_f> Menu::free_lookup; std::string Menu::display_stub_default; display_f Menu::display; +void Menu::entries_t::insert(const std::string &code, + const std::string &prompt, callback_f callback) { + char *buffer = new char[prompt.size() + 1]; + strcpy(buffer, prompt.c_str()); + + items.emplace_back(callback, buffer); + codes.emplace_back(code, prompt); +} + +const std::string &Menu::getCode(std::size_t idx) const { + return entries.codes[idx].code; +} + +const std::string &Menu::getPrompt(std::size_t idx) const { + return entries.codes[idx].prompt; +} + +callback_f Menu::getCallback(std::size_t idx) const { + return entries.items[idx].callback; +} + void Menu::read(const std::string &s) { std::string line, delim, code, prompt; std::fstream fs(s); - char tmp = 0; auto last = menu_lookup.end(); while (std::getline(fs, line)) { if (line.empty()) continue; + std::istringstream ss(line); - ss >> delim >> code; - ss.ignore(1, ' '), std::getline(ss, prompt); - if (delim == "+") { + ss >> delim >> code >> std::ws; + std::getline(ss, prompt); + + if (delim != "+") last->second.entries.insert(code, prompt); + else { const auto [iter, succ] = menu_lookup.emplace( std::piecewise_construct, std::forward_as_tuple(code), std::forward_as_tuple(private_ctor_t{}, code, prompt)); last = iter; - } else { - last->second.entries.insert(code, prompt); } } } -void Menu::print(const std::string &code, const int depth) { - const auto it = menu_lookup.find(code); - if (it == menu_lookup.end()) return; - const Menu *menu = &it->second; - - if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code); - - for (int i = 0; i < menu->getSize(); i++) { - std::cout << std::string(depth << 1, ' '); - std::cout << menu->getPrompt(i); - std::cout << std::format(" ({})\n", menu->getCode(i)); - menu->print(code, depth + 1); - } -} - int Menu::display_stub(int idx) { static std::deque<const Menu *> st; const std::string &code = - st.size() ? st.back()->getCode(idx) : display_stub_default; + !st.empty() ? st.back()->getCode(idx) : display_stub_default; const auto ml_it = menu_lookup.find(code); if (ml_it != menu_lookup.end()) { @@ -68,7 +74,7 @@ int Menu::display_stub(int idx) { } const auto fl_it = free_lookup.find(code); - if (fl_it != free_lookup.end()) { return fl_it->second(0); } + if (fl_it != free_lookup.end()) return fl_it->second(0); std::cout << "Stamen: nothing to do..." << std::endl; return 1; diff --git a/src/stamen.cpp b/src/stamen.cpp @@ -7,7 +7,6 @@ #include <ostream> #include <variant> - namespace stamen { void read(const char *filename) { Menu::read(filename); } @@ -21,15 +20,14 @@ int dynamic(const char *code, display_f display) { } int builtin_display(const char *title, const item_t itemv[], int size) { - const size_t digits = size_t(std::log10(size)) + 1; const auto items = std::span(itemv, size_t(size)); + const size_t dgts = size_t(std::log10(size)) + 1; int choice = 0; while (true) { std::cout << std::format("{}:\n", title); for (auto i = 0ul; i < size; i++) { - std::cout << std::format(" {:{}}. {}\n", i, digits, - items[i].prompt); + std::cout << std::format(" {:{}}. {}\n", i, dgts, items[i].prompt); } while (true) { @@ -40,20 +38,18 @@ int builtin_display(const char *title, const item_t itemv[], int size) { return 1; } - std::cout << std::format("Choice: {}\n\n", - items[choice].prompt); + std::cout << "Choice: " << items[choice].prompt << "\n\n"; const int res = items[choice].callback(choice); - if (res > 1) return res - 1; - else break; + if (res < 2) break; + return res - 1; } else if (std::cin.eof()) { std::cerr << "encountered end of input!\n"; return std::numeric_limits<int>::max(); - } else { - std::cin.clear(); - std::cin.ignore(std::numeric_limits<std::streamsize>::max(), - '\n'); } + + std::cin.clear(); + std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); std::cout << "Invalid option, please choose again!\n"; } std::cout << std::endl;