From cab5db18a4b332db9ac9ba05dec49a9335d6ab47 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Sun, 15 Dec 2024 19:53:40 +0100 Subject: [PATCH] Initial Commit --- .vscode/settings.json | 14 +++ LICENSE | 21 ++++ README.md | 5 + include/cli-fancy.hpp | 267 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 307 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 LICENSE create mode 100644 README.md create mode 100644 include/cli-fancy.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3fe13be --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "files.associations": { + "cstring": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "random": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "iostream": "cpp" + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..531f04a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 René Amthor (tobid7) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5f3cb7a --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# cli-fancy library + +Single header library for cli apps + +Used in ctrff and npi-build \ No newline at end of file diff --git a/include/cli-fancy.hpp b/include/cli-fancy.hpp new file mode 100644 index 0000000..c946cbb --- /dev/null +++ b/include/cli-fancy.hpp @@ -0,0 +1,267 @@ +#pragma once + +/* +MIT License + +Copyright (c) 2024 René Amthor (tobid7) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +// CLI FANCY +namespace cf7 { +namespace sym { +const std::string arrow = ""; +} +class col { + public: + col() { m_isres = true; } + col(bool fg, unsigned char r, unsigned char g, unsigned char b) { + m_isres = false; + m_r = r; + m_g = g; + m_b = b; + m_fg = fg; + } + col(unsigned char r, unsigned char g, unsigned char b) { + m_isres = false; + m_r = r; + m_g = g; + m_b = b; + // Default Fancy Api uses GetAs + m_fg = false; + } + ~col() = default; + + std::string Get() const { return GetAs(m_fg); } + + std::string GetAs(bool fg) const { + if (m_isres) { + return "\033[00m"; + } + std::string col = "\033["; + col += fg ? "38" : "48"; + col += ";2;"; + col += std::to_string(m_r) + ";"; + col += std::to_string(m_g) + ";"; + col += std::to_string(m_b) + "m"; + return col; + } + + operator std::string() const { return Get(); } + operator const char*() const { + /// CREATE DYNAMIC BUFFER TO ALWAYS RETURN STABLE CONST CHAR + static char buf[32]; + strcpy(buf, Get().c_str()); + buf[sizeof(buf) - 1] = '\0'; + return buf; + } + + private: + bool m_isres = true; + bool m_fg = false; + unsigned char m_r = 0; + unsigned char m_g = 0; + unsigned char m_b = 0; +}; +void PrintFancy(const std::vector>& e) { + std::cout << col(); + for (int i = 0; i < e.size(); i++) { + std::cout << e[i].second.GetAs(false) << col(true, 255, 255, 255); + std::cout << " " << e[i].first << " "; + if (i == e.size() - 1) { + std::cout << col() << e[i].second.GetAs(true) << sym::arrow; + } else { + std::cout << e[i + 1].second.GetAs(false) << e[i].second.GetAs(true) + << sym::arrow; + } + } + std::cout << col() << std::endl; +} +class command { + public: + using ArgumentList = std::vector>; + using Function = std::function; + class sub { + public: + sub(const std::string& _short, const std::string& _long, + const std::string desc, bool req) { + m_isrequired = req; + m_long = _long; + m_short = _short; + m_desc = desc; + } + ~sub() = default; + + bool IsRequired() const { return m_isrequired; } + const std::string& GetShort() const { return m_short; } + const std::string& GetLong() const { return m_long; } + const std::string& GetDesc() const { return m_desc; } + + private: + bool m_isrequired; + std::string m_long; + std::string m_short; + std::string m_desc; + }; + command(std::string base, std::string desc) { + m_name = base; + m_desc = desc; + } + ~command() = default; + + command& AddSubEntry(const sub& sub) { + m_sub.push_back(sub); + return *this; + } + + command& SetFunction(Function fun) { + m_fun = fun; + return *this; + } + + Function Func() const { return m_fun; } + + static std::string GetArg(ArgumentList list, std::string arg) { + for (const auto& it : list) { + if (it.first == arg) { + return it.second; + } + } + return ""; + } + + const std::string& GetName() const { return m_name; } + const std::string& GetDesc() const { return m_desc; } + const std::vector& GetArgs() const { return m_sub; } + + private: + std::string m_name; + std::string m_desc; + std::vector m_sub; + Function m_fun; +}; +class arg_mgr { + public: + arg_mgr() {} + arg_mgr(char** args, int argc) { Parse(args, argc); } + arg_mgr(int argc, char** args) { Parse(args, argc); } + ~arg_mgr() = default; + + void SetAppInfo(const std::string& name, const std::string& ver) { + app_name = name; + app_version = ver; + } + + bool FindString(const std::vector& l, std::string w) { + auto tf = "-" + w; + return (std::find(l.begin(), l.end(), tf) != l.end()); + } + + std::string GetArg(std::string w, std::string def = "") { + if (!FindString(m_args, w)) return def; + for (size_t i = 0; i < m_args.size() - 1; i++) { + if (m_args[i] == std::string("-" + w)) { + return m_args[i + 1]; + } + } + return def; + } + void Parse(char** args, int argc) { + for (int i = 0; i < argc; i++) { + m_args.push_back(args[i]); + } + } + + void PrintHelp(const std::string& cmd = "") { + if (!app_name.empty() && !app_version.empty()) { + PrintFancy({ + std::make_pair(app_name, col(20, 20, 20)), + std::make_pair(app_version, col(50, 50, 50)), + }); + } + PrintFancy({ + std::make_pair("Build", col(40, 40, 40)), + std::make_pair(std::string(__TIMESTAMP__), col(70, 70, 70)), + }); + if (!cmd.empty()) { + for (const auto& c : m_commands) { + if (c.GetName() == cmd) { + PrintFancy({std::make_pair(c.GetName(), col(10, 10, 10)), + std::make_pair("Help", col(30, 30, 30))}); + for (const auto& it : c.GetArgs()) { + PrintFancy({ + std::make_pair("-" + it.GetShort(), col(30, 30, 30)), + std::make_pair("--" + it.GetLong(), col(50, 50, 50)), + std::make_pair(it.GetDesc(), col(70, 70, 70)), + std::make_pair(it.IsRequired() ? "true" : "false", + col(90, 90, 90)), + }); + } + return; + } + } + } + PrintFancy({std::make_pair("Commands", col(30, 30, 30))}); + for (command c : m_commands) { + PrintFancy({ + std::make_pair(c.GetName(), col(40, 40, 40)), + std::make_pair(c.GetDesc(), col(60, 60, 60)), + }); + } + } + + void Execute() { + command::ArgumentList arglist; + for (const auto& c : m_commands) { + if (c.GetName() == m_args[1]) { + for (const auto& j : c.GetArgs()) { + if (!FindString(m_args, j.GetShort())) { + if (j.IsRequired()) { + PrintHelp(c.GetName()); + return; + } + } else { + arglist.push_back( + std::make_pair(j.GetShort(), GetArg(j.GetShort()))); + } + } + c.Func()(arglist); + return; + } + } + PrintHelp(""); + } + + void AddCommand(command cmd) { m_commands.push_back(cmd); } + + private: + std::string app_name; + std::string app_version; + std::vector m_args; + std::vector m_commands; +}; +} // namespace cf7 \ No newline at end of file