From c2837868a1b5d310dc660bd74d45208b299d25db Mon Sep 17 00:00:00 2001 From: EAS Barbosa Date: Fri, 9 Aug 2024 02:00:11 -0300 Subject: [PATCH] feat: edit/view configurations --- CHANGELOG.md | 4 ++ README.md | 26 ++++++-- TODO.md | 7 +- meson.build | 2 +- src/database/parse.cpp | 68 +++++++++++++++---- src/database/repository.cpp | 4 -- src/handlers/actions.cpp | 2 +- src/handlers/commands.cpp | 126 ++++++++++++++++++++++++++++++------ src/handlers/helpers.cpp | 22 +++---- src/include/cli.hpp | 33 ++++++++++ src/include/commands.hpp | 10 ++- src/include/konfig.hpp | 26 +++++++- src/include/parse.hpp | 5 ++ src/include/project.hpp | 39 ++++++++++- src/main.cpp | 31 ++++++--- 15 files changed, 336 insertions(+), 69 deletions(-) create mode 100644 src/include/cli.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c6b759..c2afcac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ along with Onur. If not, see . # CHANGELOG +## 0.3.0 + +- feat: edit/view configurations + ## 0.2.0 - feat: refactor, more tests and improve cli ui diff --git a/README.md b/README.md index 21e21db..a95d6b2 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,30 @@ Easily manage multiple FLOSS repositories. | [ruby](https://gitlab.com/easbarba/onur-ruby) - ## Usage ```shell -onur grab -onur backup nuxt awesomewm gitignore +# grab all projects +onur grab + +# grab only the c projects +onur grab c + +# back up these projects as tar.gz +onur backup ecmascript.nuxt lua.awesomewm misc.gitignore + +# list the cpp configuration file +onur config cpp + +# list topics of haskell +onur config haskell. + +# list only the projects on misc topic of lisp +onur config lisp.misc + +# add a new configuration with theses entries in the topic misc of c +onur config c.misc cli11 https://github.com/cliutils/cli11 main + onur --help ``` @@ -90,7 +108,7 @@ depth = 1 ## Installation -`onur` requires a c++ 20 compiler and `meson` to install, locally at `$HOME/.local/bin`, easily with : `make all install`. +`onur` requires a c++ 23 compiler and `meson` to install, locally at `$HOME/.local/bin`, easily with : `make all install`. ## Development diff --git a/TODO.md b/TODO.md index b2cd28a..ba6cda5 100644 --- a/TODO.md +++ b/TODO.md @@ -17,12 +17,15 @@ along with Onur. If not, see . ### High -- verbose +- flag: --verbose - validation of repository links -- actions: --filter rust +- actions: --filter rust:misc - actions: --only rust,haskel,commonlisp - actions: --exclude rust,haskel,commonlisp +### Mid +- actions: onur config c.misc foo https://git@gmasd main + ### Low - config: move on these to a syntax check class diff --git a/meson.build b/meson.build index 5c8a876..8b18684 100644 --- a/meson.build +++ b/meson.build @@ -16,7 +16,7 @@ project( 'cpp', license: 'GNU GPL version 3', version: '0.2.0', - default_options: [ 'cpp_std=c++20', 'warning_level=3' ], + default_options: [ 'cpp_std=c++23', 'warning_level=3' ], meson_version: '>=1.4.0', ) diff --git a/src/database/parse.cpp b/src/database/parse.cpp index 450a80b..326ef0c 100644 --- a/src/database/parse.cpp +++ b/src/database/parse.cpp @@ -17,10 +17,11 @@ #include #include #include -#include - #include +#include +#include +#include "../include/cli.hpp" #include "../include/konfig.hpp" #include "../include/parse.hpp" #include "../include/project.hpp" @@ -52,29 +53,29 @@ auto Parse::single (path filepath) -> Konfig { Konfig result; - map > subtopiks; + map> _topics; auto configParsed = parse_file (contents_of (filepath)); - result.topic = { filepath.stem () }; + result.name = { filepath.stem () }; - for (auto &[subtopic, subtopics] : configParsed.items ()) + for (auto &[topic, topics] : configParsed.items ()) { list projects; - for (auto projekt : subtopics) + for (auto project : topics) { string branch{ "master" }; - if (!projekt["branch"].is_null ()) - branch = projekt["branch"]; + if (!project["branch"].is_null ()) + branch = project["branch"]; - auto pkt{ Project (projekt["name"], projekt["url"], branch) }; + auto pkt{ Project (project["name"], project["url"], branch) }; projects.push_back (pkt); } - subtopiks[subtopic] = { projects }; + _topics[topic] = { projects }; } - result.subtopics = { subtopiks }; + result.topics = { _topics }; return result; } @@ -90,3 +91,48 @@ Parse::contents_of (string path_to_file) -> string ifstream file (path_to_file); return { istreambuf_iterator (file), istreambuf_iterator{} }; } + +auto +Parse::exist (std::string name) -> bool +{ + for (auto config : multi ()) + if (config.name == name) + return true; + + // std::for_each (multi ().begin (), multi ().end (), + // [name, &result] (Konfig config) { + // std::println ("MEH"); + // result = { config.name == name }; + // std::println ("FOOL"); + // }); + + return false; +} + +// Overload the to_json function for automatic conversion +void +to_json (nlohmann::json &j, const Konfig &k) +{ + j = k.to_json (); +} + +auto +Parse::save (std::string name, std::string topic, + ConfigEntries entries) -> void +{ + // if (entries.name || entries.url.has_value () || !entries.branch.empty ()) + // { + // std::println ("Either name or url of project are missing. Exiting!"); + // return; + // } + + Project project{ entries }; + + std::map> topics; + topics[topic] = { entries }; + + Konfig konfig{ name, topics }; + nlohmann::json j = konfig; + + std::println ("Saving config with name {} as \n{}", konfig.name, j.dump ()); +} diff --git a/src/database/repository.cpp b/src/database/repository.cpp index 6c6ba30..c955d71 100644 --- a/src/database/repository.cpp +++ b/src/database/repository.cpp @@ -32,8 +32,6 @@ Repository::allConfigs (void) -> list list result; Globals globals; - printf (" Configurations: ["); - for (auto config : directory_iterator (globals.onurDir)) { if (config.path ().extension () != ".json") @@ -46,10 +44,8 @@ Repository::allConfigs (void) -> list if (file.peek () == std::ifstream::traits_type::eof ()) continue; - printf (" %s ", config.path ().stem ().c_str ()); result.push_back (config); } - printf (" ]\n"); return result; } diff --git a/src/handlers/actions.cpp b/src/handlers/actions.cpp index bba0b63..405df28 100644 --- a/src/handlers/actions.cpp +++ b/src/handlers/actions.cpp @@ -30,7 +30,7 @@ auto Actions::klone (Project project, path dirpath) -> void { auto finalCommand{ format ( - "git clone --single-branch --depth=1 --quiet {} {}", project.url, + "git clone --single-branch --depth=1 --quiet {} {}", project.Url (), dirpath.string ()) }; system (finalCommand.c_str ()); } diff --git a/src/handlers/commands.cpp b/src/handlers/commands.cpp index 8457ec6..6fb22cb 100644 --- a/src/handlers/commands.cpp +++ b/src/handlers/commands.cpp @@ -14,45 +14,46 @@ */ #include -#include -#include +#include +#include +#include +#include #include "../include/commands.hpp" -#include "../include/globals.hpp" #include "helpers.hpp" -using std::cout; -using std::endl; using std::filesystem::exists; using std::filesystem::path; Commands::Commands () {} auto -Commands::grab (void) -> void +Commands::grab (std::optional name) -> void { - for (auto single : parse.multi ()) + for (auto singleConfig : parse.multi ()) { - cout << "\n " << single.topic << ":" << endl; + if (name.has_value () && name.value () != singleConfig.name) + continue; - for (auto subtopic : single.subtopics) + std::println ("{}: ", singleConfig.name); + + for (auto topic : singleConfig.topics) { - cout << " + " << subtopic.first << endl; - for (auto project : subtopic.second) + std::println (" + {}", topic.first); + for (auto project : topic.second) { - auto placeholder{ path (globals.projectsDir / single.topic - / subtopic.first / project.name) }; - auto dirpath{ placeholder }; + auto finalpath{ path (globals.projectsDir / singleConfig.name + / topic.first / project.Name ()) }; printProjectInfo (project); - if (exists (dirpath / ".git" / "config")) - actions.pull (dirpath); + if (exists (finalpath / ".git" / "config")) + actions.pull (finalpath); else - actions.klone (project, dirpath); + actions.klone (project, finalpath); } - cout << endl; + std::println (); } } } @@ -60,5 +61,92 @@ Commands::grab (void) -> void auto Commands::backup (void) -> void { - cout << "Backing up" << endl; + std::println ("Backing up"); +} + +auto +Commands::config (std::string name, ConfigEntries entries) -> void +{ + std::string _name{ name }; + std::optional _topic; + + if (name.contains (".")) + { + std::size_t dot_positon{ name.find (".") }; + _name = { name.substr (0, dot_positon) }; + _topic = { name.substr (dot_positon + 1) }; + } + + if (!parse.exist (_name)) + { + std::println ("No configuration by {} found!", _name); + return; + } + + if (name.ends_with (".")) + { + printSingleConfig (_name, _topic, true); + return; + } + + if (!_topic.has_value ()) + { + printSingleConfig (_name, _topic); + return; + } + else + { + printSingleConfig (_name, _topic, true); + return; + } + + // std::println ( + // "Please provide a topic and entries to save a new one. Exiting!", + // _name); + + if (!entries.name.has_value () || !entries.url.has_value ()) + { + std::println ("Enter name and url entries at least. Exiting!"); + return; + } + + parse.save (_name, _topic.value (), entries); +} + +auto +foo () -> void +{ + std::print ("Configurations: ["); + // printf (" %s ", config.path ().stem ().c_str ()); + std::println (" ]\n"); +} + +void +Commands::printSingleConfig (std::string name, + std::optional &_topic, + bool onlytopics) +{ + for (auto singleConfig : parse.multi ()) + { + if (name != singleConfig.name) + continue; + + std::println ("{}:", singleConfig.name); + for (auto topic : singleConfig.topics) + { + if (onlytopics) + { + std::print (" {} ", topic.first); + continue; + } + + if (_topic.has_value () && _topic.value () != topic.first) + continue; + + std::println (" + {}", topic.first); + + for (auto project : topic.second) + printProjectInfo (project); + } + } } diff --git a/src/handlers/helpers.cpp b/src/handlers/helpers.cpp index 6e792de..653d0fe 100644 --- a/src/handlers/helpers.cpp +++ b/src/handlers/helpers.cpp @@ -14,12 +14,10 @@ */ #include -#include +#include #include "../include/helpers.hpp" -using std::format; - using namespace std; auto @@ -27,14 +25,14 @@ printProjectInfo (Project project) -> void { std::string::size_type nameLength = 27; auto nameTruncated - = project.name.length () <= nameLength - ? project.name - : project.name.substr (0, nameLength).append ("..."); + = project.Name ().length () <= nameLength + ? project.Name () + : project.Name ().substr (0, nameLength).append ("..."); std::string::size_type urlLength = 60; - auto urlTruncated = project.url.length () <= urlLength - ? project.url - : project.url.substr (0, urlLength).append ("..."); - auto message{ format ("{:5}- {:35} {:75} {}", "", nameTruncated, - urlTruncated, project.branch) }; - cout << message << endl; + auto urlTruncated + = project.Url ().length () <= urlLength + ? project.Url () + : project.Url ().substr (0, urlLength).append ("..."); + std::println ("{:3}- {:35} {:75} {}", "", nameTruncated, urlTruncated, + project.Branch ()); } diff --git a/src/include/cli.hpp b/src/include/cli.hpp new file mode 100644 index 0000000..9566313 --- /dev/null +++ b/src/include/cli.hpp @@ -0,0 +1,33 @@ +/* + * Onur is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Onur is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Onur. If not, see . + */ + +#pragma once + +#include +#include + +struct ConfigEntries +{ + std::optional name; + std::optional url; + std::string branch{ "master" }; +}; + +struct ConfigTopic +{ + std::optional name; + std::optional topic; + bool dot{ false }; +}; diff --git a/src/include/commands.hpp b/src/include/commands.hpp index 16a3dea..6588c2a 100644 --- a/src/include/commands.hpp +++ b/src/include/commands.hpp @@ -15,10 +15,13 @@ #pragma once +#include +#include + #include "actions.hpp" +#include "cli.hpp" #include "globals.hpp" #include "parse.hpp" -#include "project.hpp" class Commands { @@ -29,6 +32,9 @@ class Commands Actions actions; Parse parse; - auto grab (void) -> void; + auto grab (std::optional name) -> void; auto backup (void) -> void; + void printSingleConfig (std::string name, std::optional &_topic, + bool onlytopics = false); + auto config (std::string name, ConfigEntries edit) -> void; }; diff --git a/src/include/konfig.hpp b/src/include/konfig.hpp index 3ff1d39..336d60a 100644 --- a/src/include/konfig.hpp +++ b/src/include/konfig.hpp @@ -18,13 +18,35 @@ #include #include +#include + #include "project.hpp" class Konfig { public: + Konfig (std::string &name, std::map> &topics) + : name (name), topics (topics) {}; + Konfig (); - std::string topic; - std::map > subtopics; + std::string name; + std::map> topics; + + nlohmann::json + to_json () const + { + nlohmann::json j; + j["name"] = name; + j["topics"] = nlohmann::json::object (); + for (const auto &[topic, projects] : topics) + { + j["topics"][topic] = nlohmann::json::array (); + for (const auto &project : projects) + { + j["topics"][topic].push_back (project.to_json ()); + } + } + return j; + } }; diff --git a/src/include/parse.hpp b/src/include/parse.hpp index 8fd7b88..3b1645a 100644 --- a/src/include/parse.hpp +++ b/src/include/parse.hpp @@ -20,6 +20,7 @@ #include +#include "cli.hpp" #include "konfig.hpp" #include "repository.hpp" @@ -34,4 +35,8 @@ class Parse auto single (std::filesystem::path filepath) -> Konfig; auto parse_file (std::string jsonString) -> nlohmann::basic_json<>; auto contents_of (std::string path_to_file) -> std::string; + auto exist (std::string name) -> bool; + auto dump () -> void; + auto save (std::string name, std::string topic, + ConfigEntries entries) -> void; }; diff --git a/src/include/project.hpp b/src/include/project.hpp index 4593e70..7a9095b 100644 --- a/src/include/project.hpp +++ b/src/include/project.hpp @@ -17,13 +17,46 @@ #include +#include + +#include "cli.hpp" + class Project { + std::string name; + std::string url; + std::string branch; + public: Project (std::string name, std::string url, std::string branch) : name (name), url (url), branch (branch) {}; - std::string name; - std::string url; - std::string branch; + Project (ConfigEntries entries) + : name (entries.name.value ()), url (entries.url.value ()), + branch (entries.branch) {}; + + std::string + Name () + { + return name; + } + + std::string + Url () + { + return url; + } + std::string + Branch () + { + return branch; + } + + nlohmann::json + to_json () const + { + return nlohmann::json{ { "name", name }, + { "url", url }, + { "branch", branch } }; + } }; diff --git a/src/main.cpp b/src/main.cpp index 5a5120f..2d5dc4d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,11 +13,13 @@ * along with Onur. If not, see . */ +#include #include #include "CLI/Option.hpp" #include +#include "include/cli.hpp" #include "include/commands.hpp" using std::string; @@ -26,30 +28,43 @@ auto main (int argc, char *argv[]) -> int { CLI::App app{ "Easily manage multiple FLOSS repositories." }; + app.require_subcommand (1); argv = { app.ensure_utf8 (argv) }; string filename{ "default" }; app.add_option ("-f,--file", filename, "A help string"); - app.set_version_flag ("--version", string ("0.0.1")); + app.set_version_flag ("--version", string ("0.2.0")); CLI::App *grab_cmd{ app.add_subcommand ("grab", "grab all projects") }; + std::optional grab_config; + grab_cmd->add_option ("config", grab_config, "Config name"); + CLI::App *backup_cmd{ app.add_subcommand ( "backup", "compress all selected projects") }; - app.require_subcommand (); + + CLI::App *config_cmd{ app.add_subcommand ("config", + "manage configurations") }; + std::string config_edit; + ConfigEntries edit; + config_cmd + ->add_option ("config", config_edit, "Configuration name and topic") + ->required (true); + config_cmd->add_option ("name", edit.name, "Project name"); + config_cmd->add_option ("url", edit.url, "Project url"); + config_cmd->add_option ("branch", edit.branch, "Project branch"); CLI11_PARSE (app, argc, argv); Commands commands; if (*grab_cmd) - { - commands.grab (); - } + commands.grab (grab_config); if (*backup_cmd) - { - commands.backup (); - } + commands.backup (); + + if (*config_cmd) + commands.config (config_edit, edit); return 0; }