stamenStatic Menu Generator | 
          
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | 69b2fecb3c6afe1358abb309dd8a3a95cdf417e7 | 
| parent | 8af1469e5448a18f63b83bf7b144d18d87940f5b | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Thu, 23 Nov 2023 19:34:45 +0000 | 
Consistency Improvement
* Move generation code to generate.cpp
* Improve exception safety
* Improve decomposition
| M | CMakeLists.txt | | | + - | 
| M | include/stamen.h | | | +++++++++++++++++++++ ------------------------------------------------------------- | 
| M | src/generate.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -- | 
3 files changed, 78 insertions(+), 69 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -3,7 +3,7 @@ 
          set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
        
        
          project(
              Stamen
              VERSION 0.0.10
              VERSION 0.0.11
              DESCRIPTION "Static menu generator"
              LANGUAGES CXX
          )
        
        diff --git a/ include/stamen.h b/ include/stamen.h
          @@ -15,6 +15,7 @@ 
          namespace stamen {
        
        
          class Menu {
          public:
            friend class Generator;
            typedef int (*callback_f)(void);
            Menu(const Menu &) = delete;
        
        
          @@ -22,35 +23,27 @@ 
          public:
        
        
            struct private_ctor_t {};
            // Tag type dispatch
            Menu(private_ctor_t, const std::string &code, const std::string &prompt)
                : Menu(code, prompt) {}
            Menu(private_ctor_t, const std::string &code, const callback_f callback)
                : Menu(code, callback) {}
            class EMenu : std::exception {
              virtual const char *what() const noexcept override {
                return "Trying to access an unknown menu";
              }
            };
            struct item_t {
              friend class Menu;
              item_t(const callback_f func, const std::string &prompt)
                  : callback(func), prompt(prompt) {}
              item_t(const std::string &code, const std::string &prompt)
                  : code(code), prompt(prompt) {}
              const std::string getCode(void) const { return code; }
              const std::string getPrompt(void) const { return prompt; }
              const callback_f getCallback(void) const { return callback; }
              int operator()(void) const {
                return callback ? callback() : getMenu(code)();
              }
              int operator()(void) const { return callback ? callback() : start(code); }
            private:
              item_t(const std::string &code, const std::string &prompt)
                  : code(code), prompt(prompt) {}
              const std::string prompt, code;
              const callback_f callback = nullptr;
            };
        
        
          @@ -61,11 +54,12 @@ 
          public:
        
        
            static void read(const std::string &s);
            static void insert(const std::string &code, const callback_f callback);
            static int start(const std::string &entry) { return getMenu(entry)(); }
            static void print(const std::string &entry) { print(entry, 1); }
            static void generateSource(std::ostream &os);
            static void generateInclude(std::ostream &os);
            static int start(const std::string &entry) {
              const Menu *menu = getMenu(entry);
              if (!menu) return 1;
              return menu->operator()();
            }
            int operator()() const {
              return callback ? callback() : display(title, items.data(), items.size());
        
        
          @@ -86,11 +80,11 @@ 
          private:
        
        
            static void print(const std::string &entry, const int depth);
            static const Menu &getMenu(const std::string &code) {
            static const Menu *getMenu(const std::string &code) {
              static lookup_t &lookup = getLookup();
              const auto it = lookup.find(code);
              if (it == lookup.end()) throw EMenu();
              return it->second;
              if (it == lookup.end()) return nullptr;
              return &it->second;
            }
            const std::string code, title;
        
        
          @@ -126,55 +120,17 @@ 
          inline void Menu::insert(const std::string &code, const callback_f callback) {
        
        
                                std::forward_as_tuple(private_ctor_t{}, code, callback));
          }
          inline void Menu::generateInclude(std::ostream &os) {
            os << "#ifndef STAMEN_MENU_H\n";
            os << "#define STAMEN_MENU_H\n\n";
            os << "#include <stamen.h>\n\n";
            os << "namespace stamen {\n\n";
            for (const auto &[code, _] : getLookup()) {
              const Menu &menu = getMenu(code);
              if (menu.callback) continue;
              os << std::format("int {}(void);\n", menu.code);
            }
            os << "\n}\n";
            os << "#endif\n";
          }
          inline void Menu::generateSource(std::ostream &os) {
            os << "#include <stamen.h>\n";
            os << "#include \"shared.h\"\n";
            os << "\nnamespace stamen {\n";
            for (const auto &[code, _] : getLookup()) {
              const Menu &menu = getMenu(code);
              if (menu.callback) continue;
              os << std::format("int {}(void) {{\n", menu.code);
              os << std::format("\tstatic const Menu::item_t items[] = {{\n");
              for (const auto &item : menu.items) {
                os << std::format("\t\t{{{}, \"{}\"}},\n", item.code, item.prompt);
              }
              os << std::format("\t}};\n");
              os << std::format("\treturn Menu::display(\"{}\", items, "
                                "sizeof(items) / sizeof(Menu::item_t));\n",
                                menu.title);
              os << std::format("}}\n\n");
            }
            os << "\n}\n";
          }
          inline void Menu::print(const std::string &code, const int depth) {
            static lookup_t &lookup = getLookup();
            const auto it = lookup.find(code);
            if (it == lookup.end()) return;
            const Menu &menu = it->second;
            const Menu *menu = getMenu(code);
            if (!menu) return;
            if (depth == 1) std::cout << std::format("{}({})\n", menu.title, code);
            if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code);
            if (!menu.callback) {
              for (const auto &item : menu.items) {
            if (!menu->callback) {
              for (const auto &item : menu->items) {
                std::cout << std::format("{}{} ({})\n", std::string(depth << 1, ' '),
                                         item.prompt, item.code);
                menu.print(item.code, depth + 1);
                                         item.getPrompt(), item.getCode());
                menu->print(item.getCode(), depth + 1);
              }
            }
          }
        
        diff --git a/ src/generate.cpp b/ src/generate.cpp
@@ -3,6 +3,58 @@
using namespace stamen;
          namespace stamen {
          class Generator {
          public:
            class EGenerate : std::exception {
              virtual const char *what() const noexcept override {
                return "Trying to access unknown code";
              }
            };
            static void generateInclude(std::ostream &os) {
              os << "#ifndef STAMEN_MENU_H\n";
              os << "#define STAMEN_MENU_H\n\n";
              os << "#include <stamen.h>\n\n";
              os << "namespace stamen {\n\n";
              for (const auto &[code, _] : Menu::getLookup()) {
                const Menu *menu = Menu::getMenu(code);
                if (!menu) throw EGenerate();
                if (menu->callback) continue;
                os << std::format("int {}(void);\n", menu->code);
              }
              os << "\n}\n";
              os << "#endif\n";
            }
            static void generateSource(std::ostream &os) {
              os << "#include <stamen.h>\n";
              os << "#include \"shared.h\"\n";
              os << "\nnamespace stamen {\n";
              for (const auto &[code, _] : Menu::getLookup()) {
                const Menu *menu = Menu::getMenu(code);
                if (!menu) throw EGenerate();
                if (menu->callback) continue;
                os << std::format("int {}(void) {{\n", menu->code);
                os << std::format("\tstatic const Menu::item_t items[] = {{\n");
                for (const auto &item : menu->items) {
                  os << std::format("\t\t{{{}, \"{}\"}},\n", item.getCode(),
                                    item.getPrompt());
                }
                os << std::format("\t}};\n");
                os << std::format("\treturn Menu::display(\"{}\", items, "
                                  "sizeof(items) / sizeof(Menu::item_t));\n",
                                  menu->title);
                os << std::format("}}\n\n");
              }
              os << "\n}\n";
            }
          };
          } // namespace stamen
          int main(const int argc, const char *argv[]) {
            if (argc != 2) {
              std::cout << "please enter exaclty one config file" << std::endl;
        
        
          @@ -15,8 +67,9 @@ 
          int main(const int argc, const char *argv[]) {
        
        
            std::string::size_type pos = path.rfind('.');
            std::string base = pos != std::string::npos ? path.substr(0, pos) : path;
            std::ofstream cpp(base + ".cpp"), h(base + ".h");
            Menu::generateSource(cpp);
            Menu::generateInclude(h);
            Generator::generateSource(cpp);
            Generator::generateInclude(h);
            return 0;
          }