mirror of
https://github.com/davidallendj/gdpm.git
synced 2025-12-20 03:27:02 -07:00
Refactored, simplified, and removed code; replaced cli header lib
- Reinitialized submodules - Slightly restructed project - Added more options to `compile.sh` script - Added more utility functions
This commit is contained in:
parent
d34243db74
commit
e48c54aa40
27 changed files with 564 additions and 3232 deletions
|
|
@ -21,6 +21,7 @@
|
|||
#include <fmt/printf.h>
|
||||
#include <rapidjson/document.h>
|
||||
#include <cxxopts.hpp>
|
||||
#include "clipp.h"
|
||||
|
||||
#include <rapidjson/ostreamwrapper.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
|
|
@ -35,25 +36,23 @@
|
|||
*/
|
||||
|
||||
namespace gdpm::package_manager{
|
||||
remote::repository_map remote_sources;
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
config::context config;
|
||||
rest_api::context api_params;
|
||||
action_e action;
|
||||
string_list packages;
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
config::context config;
|
||||
remote::repository_map remote_sources;
|
||||
action_e action;
|
||||
|
||||
// opts_t opts;
|
||||
bool skip_prompt = false;
|
||||
bool clean_tmp_dir = false;
|
||||
int priority = -1;
|
||||
bool skip_prompt = false;
|
||||
bool clean_tmp_dir = false;
|
||||
int priority = -1;
|
||||
|
||||
|
||||
result_t<exec_args> initialize(int argc, char **argv){
|
||||
error initialize(int argc, char **argv){
|
||||
// curl_global_init(CURL_GLOBAL_ALL);
|
||||
curl = curl_easy_init();
|
||||
config = config::make_context();
|
||||
api_params = rest_api::make_context();
|
||||
action = action_e::none;
|
||||
curl = curl_easy_init();
|
||||
config = config::make_context();
|
||||
action = action_e::none;
|
||||
|
||||
/* Check for config and create if not exists */
|
||||
if(!std::filesystem::exists(config.path)){
|
||||
|
|
@ -62,287 +61,190 @@ namespace gdpm::package_manager{
|
|||
error error = config::load(config.path, config);
|
||||
if(error.has_occurred()){
|
||||
log::error(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Create the local databases if it doesn't exist already */
|
||||
error = cache::create_package_database();
|
||||
if(error.has_occurred()){
|
||||
log::error(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Run the rest of the program then exit */
|
||||
cxxargs args = _parse_arguments(argc, argv);
|
||||
return _handle_arguments(args);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int execute(const exec_args& in){
|
||||
run_command(action, in.args, in.opts);
|
||||
if(clean_tmp_dir)
|
||||
package::clean_temporary(config, packages);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void finalize(){
|
||||
error finalize(){
|
||||
curl_easy_cleanup(curl);
|
||||
error error = config::save(config.path, config);
|
||||
if(error()){
|
||||
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
cxxargs _parse_arguments(int argc, char **argv){
|
||||
/* Parse command-line arguments using cxxopts */
|
||||
cxxopts::Options options(
|
||||
argv[0],
|
||||
"Experimental package manager made for managing assets for the Godot game engine through the command-line.\n"
|
||||
);
|
||||
options.allow_unrecognised_options();
|
||||
options.custom_help("[COMMAND] [OPTIONS...]");
|
||||
options.add_options("Command")
|
||||
("command", "Specify the input parameters", cxxopts::value<string>())
|
||||
("positional", "", cxxopts::value<string_list>())
|
||||
("install", "Install package or packages.", cxxopts::value<string_list>()->implicit_value(""), "<packages...>")
|
||||
("remove", "Remove a package or packages.", cxxopts::value<string_list>()->implicit_value(""), "<packages...>")
|
||||
("update", "Update a package or packages. This will update all packages if no argument is provided.", cxxopts::value<string_list>()->implicit_value(""), "<packages...>")
|
||||
("search", "Search for a package or packages.", cxxopts::value<string_list>(), "<packages...>")
|
||||
("export", "Export list of packages", cxxopts::value<string>()->default_value("./gdpm-packages.txt"))
|
||||
("list", "Show list of installed packages.")
|
||||
("link", "Create a symlink (or shortcut) to target directory. Must be used with the `--path` argument.", cxxopts::value<string_list>(), "<packages...>")
|
||||
("clone", "Clone packages into target directory. Must be used with the `--path` argument.", cxxopts::value<string_list>(), "<packages...>")
|
||||
("cache", "Caching operations", cxxopts::value<string_list>())
|
||||
("clean", "Clean temporary downloaded files.")
|
||||
("fetch", "Fetch asset data from remote sources.")
|
||||
("remote", "Set a source repository.", cxxopts::value<string>()->default_value(constants::AssetRepo), "<url>")
|
||||
("h,help", "Print this message and exit.")
|
||||
("version", "Show the current version and exit.")
|
||||
;
|
||||
options.parse_positional({"command", "positional"});
|
||||
options.positional_help("");
|
||||
options.add_options("Other")
|
||||
("c,config", "Set the config file path.", cxxopts::value<string>())
|
||||
("f,file", "Read file to install or remove packages.", cxxopts::value<string>(), "<path>")
|
||||
("path", "Specify a path to use with a command", cxxopts::value<string_list>())
|
||||
("type", "Set package type (any|addon|project).", cxxopts::value<string>())
|
||||
("sort", "Sort packages in order (rating|cost|name|updated).", cxxopts::value<string>())
|
||||
("support", "Set the support level for API (all|official|community|testing).")
|
||||
("max-results", "Set the max results to return from search.", cxxopts::value<int>()->default_value("500"), "<int>")
|
||||
("godot-version", "Set the Godot version to include in request.", cxxopts::value<string>())
|
||||
("package-dir", "Set the local package storage location.", cxxopts::value<string>())
|
||||
("tmp-dir", "Set the local temporary storage location.", cxxopts::value<string>())
|
||||
("timeout", "Set the amount of time to wait for a response.", cxxopts::value<size_t>())
|
||||
("no-sync", "Disable synchronizing with remote.", cxxopts::value<bool>()->implicit_value("true")->default_value("false"))
|
||||
("y,no-prompt", "Bypass yes/no prompt for installing or removing packages.")
|
||||
("v,verbose", "Show verbose output.", cxxopts::value<int>()->implicit_value("1")->default_value("0"), "0-5")
|
||||
;
|
||||
|
||||
auto result = options.parse(argc, argv);
|
||||
return {result, options};
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void insert(
|
||||
var_opts& opts,
|
||||
const cxxopts::ParseResult& result,
|
||||
const string& key
|
||||
){
|
||||
opts.insert(var_opt(key, result[key].as<T>()));
|
||||
};
|
||||
|
||||
|
||||
result_t<exec_args> _handle_arguments(const cxxargs& args){
|
||||
const auto& result = args.result;
|
||||
const auto& options = args.options;
|
||||
exec_args in;
|
||||
error parse_arguments(int argc, char **argv){
|
||||
/* Replace cxxopts with clipp */
|
||||
action_e action = action_e::none;
|
||||
package::title_list package_titles;
|
||||
string_list input;
|
||||
package::params params;
|
||||
args_t args;
|
||||
var_opts opts;
|
||||
|
||||
// auto get_opt = [](const string& key){
|
||||
// result[key].as<T>();
|
||||
// }
|
||||
// auto get_opt = [&result]<typename V>(const string& key){
|
||||
// return opt(key, result[key].as<V>());
|
||||
// };
|
||||
/* Set global options */
|
||||
auto configOpt = clipp::option("--config-path").set(config.path)% "set config path";
|
||||
auto fileOpt = clipp::option("--file", "-f").set(input) % "read file as input";
|
||||
auto pathOpt = clipp::option("--path").set(params.paths) % "specify a path to use with command";
|
||||
auto typeOpt = clipp::option("--type").set(config.info.type) % "set package type (any|addon|project)";
|
||||
auto sortOpt = clipp::option("--sort").set(config.api_params.sort) % "sort packages in order (rating|cost|name|updated)";
|
||||
auto supportOpt = clipp::option("--support").set(config.api_params.support) % "set the support level for API (all|official|community|testing)";
|
||||
auto maxResultsOpt = clipp::option("--max-results").set(config.api_params.max_results) % "set the request max results";
|
||||
auto godotVersionOpt = clipp::option("--godot-version").set(config.api_params.godot_version) % "set the request Godot version";
|
||||
auto packageDirOpt = clipp::option("--package-dir").set(config.packages_dir) % "set the global package location";
|
||||
auto tmpDirOpt = clipp::option("--tmp-dir").set(config.tmp_dir) % "set the temporary download location";
|
||||
auto timeoutOpt = clipp::option("--timeout").set(config.timeout) % "set the request timeout";
|
||||
auto verboseOpt = clipp::option("--verbose", "-v").set(config.verbose) % "show verbose output";
|
||||
|
||||
/* Set the options */
|
||||
auto cleanOpt = clipp::option("--clean").set(config.clean_temporary) % "enable/disable cleaning temps";
|
||||
auto parallelOpt = clipp::option("--jobs").set(config.jobs) % "set number of parallel jobs";
|
||||
auto cacheOpt = clipp::option("--enable-cache").set(config.enable_cache) % "enable/disable local caching";
|
||||
auto syncOpt = clipp::option("--enable-sync").set(config.enable_sync) % "enable/disable remote syncing";
|
||||
auto skipOpt = clipp::option("--skip-prompt").set(config.skip_prompt) % "skip the y/n prompt";
|
||||
auto remoteOpt = clipp::option("--remote").set(params.remote_source) % "set remote source to use";
|
||||
|
||||
auto packageValues = clipp::values("packages", package_titles);
|
||||
auto requiredPath = clipp::required("--path", input);
|
||||
|
||||
/* Set option variables first to be used in functions below. */
|
||||
if(result.count("search")){
|
||||
in.args = result["search"].as<var_args>();
|
||||
}
|
||||
if(result.count("help")){
|
||||
log::println("{}", options.help());
|
||||
}
|
||||
if(result.count("config")){
|
||||
config.path = result["config"].as<string>();
|
||||
insert<string>(in.opts, result, "config");
|
||||
config::load(config.path, config);
|
||||
log::info("Config: {}", config.path);
|
||||
}
|
||||
if(result.count("remote")){
|
||||
string sub_command = result["remote"].as<string>();
|
||||
log::print("sub command: {}", sub_command);
|
||||
if(sub_command == "add"){
|
||||
string argv = result.arguments_string();
|
||||
log::println("argv: {}", argv);
|
||||
remote::add_repositories(config, {});
|
||||
auto installCmd = (
|
||||
clipp::command("install")
|
||||
.set(action, action_e::install)
|
||||
.doc("Install packages from asset library"),
|
||||
clipp::values("packages", package_titles),
|
||||
clipp::option("--godot-version") & clipp::value("version", config.info.godot_version),
|
||||
cleanOpt, parallelOpt, syncOpt, skipOpt, remoteOpt
|
||||
|
||||
);
|
||||
auto addCmd = (
|
||||
clipp::command("add").set(action, action_e::add)
|
||||
.doc("Add a package to a local project"),
|
||||
packageValues
|
||||
);
|
||||
auto removeCmd = (
|
||||
clipp::command("remove")
|
||||
.set(action, action_e::remove)
|
||||
.doc("Remove a package from local project"),
|
||||
packageValues
|
||||
);
|
||||
auto updateCmd = (
|
||||
clipp::command("update")
|
||||
.set(action, action_e::update)
|
||||
.doc("Update package(s)"),
|
||||
packageValues
|
||||
);
|
||||
auto searchCmd = (
|
||||
clipp::command("search")
|
||||
.set(action, action_e::search)
|
||||
.doc("Search for package(s)"),
|
||||
packageValues
|
||||
);
|
||||
auto exportCmd = (
|
||||
clipp::command("export")
|
||||
.set(action, action_e::p_export)
|
||||
.doc("Export package list"),
|
||||
clipp::values("path", input)
|
||||
);
|
||||
auto listCmd = (
|
||||
clipp::command("list")
|
||||
.set(action, action_e::list)
|
||||
.doc("Show installed packages")
|
||||
);
|
||||
auto linkCmd = (
|
||||
clipp::command("link")
|
||||
.set(action, action_e::link)
|
||||
.doc("Create symlink packages to project"),
|
||||
packageValues,
|
||||
requiredPath
|
||||
);
|
||||
auto cloneCmd = (
|
||||
clipp::command("clone")
|
||||
.set(action, action_e::clone)
|
||||
.doc("Clone packages to project"),
|
||||
packageValues,
|
||||
requiredPath
|
||||
);
|
||||
auto cleanCmd = (
|
||||
clipp::command("clean")
|
||||
.set(action, action_e::clean)
|
||||
.doc("Clean temporary files"),
|
||||
packageValues
|
||||
);
|
||||
auto configCmd = (
|
||||
clipp::command("config")
|
||||
.set(action, action_e::config)
|
||||
.doc("Set/get config properties")
|
||||
);
|
||||
auto fetchCmd = (
|
||||
clipp::command("fetch")
|
||||
.set(action, action_e::fetch)
|
||||
.doc("Fetch asset metadata from remote source")
|
||||
);
|
||||
auto remoteCmd = (
|
||||
clipp::command("remote")
|
||||
.set(action, action_e::remote)
|
||||
.doc("Manage remote sources")
|
||||
.required("subcommand")
|
||||
);
|
||||
auto uiCmd = (
|
||||
clipp::command("ui")
|
||||
.set(action, action_e::ui)
|
||||
.doc("Show the UI")
|
||||
);
|
||||
auto helpCmd = (
|
||||
clipp::command("help")
|
||||
.set(action, action_e::help)
|
||||
);
|
||||
|
||||
auto cli = (
|
||||
(installCmd | addCmd | removeCmd | updateCmd | searchCmd | exportCmd |
|
||||
listCmd | linkCmd | cloneCmd | cleanCmd | configCmd | fetchCmd |
|
||||
remoteCmd | uiCmd | helpCmd)
|
||||
);
|
||||
|
||||
/* Make help output */
|
||||
string map_page_format("");
|
||||
auto man_page = clipp::make_man_page(cli);
|
||||
std::for_each(man_page.begin(), man_page.end(),
|
||||
[&map_page_format](const clipp::man_page::section& s){
|
||||
map_page_format += s.title() + "\n";
|
||||
map_page_format += s.content() + "\n";
|
||||
}
|
||||
else if(sub_command == "remove"){
|
||||
remote::remove_respositories(config, {});
|
||||
log::println("argv: {}");
|
||||
);
|
||||
if(clipp::parse(argc, argv, cli)){
|
||||
switch(action){
|
||||
case action_e::install: package::install(config, package_titles, params); break;
|
||||
case action_e::add: break;
|
||||
case action_e::remove: package::remove(config, package_titles, params); break;
|
||||
case action_e::update: package::update(config, package_titles, params); break;
|
||||
case action_e::search: package::search(config, package_titles, params); break;
|
||||
case action_e::p_export: package::export_to(input); break;
|
||||
case action_e::list: package::list(config, params); break;
|
||||
/* ...opts are the paths here */
|
||||
case action_e::link: package::link(config, package_titles, params); break;
|
||||
case action_e::clone: package::clone(config, package_titles, params); break;
|
||||
case action_e::clean: package::clean_temporary(config, package_titles); break;
|
||||
case action_e::config: config::handle_config(config, package_titles, opts); break;
|
||||
case action_e::fetch: package::synchronize_database(config, package_titles); break;
|
||||
case action_e::sync: package::synchronize_database(config, package_titles); break;
|
||||
case action_e::remote: remote::handle_remote(config, args, opts); break;
|
||||
case action_e::ui: log::info("ui not implemented yet"); break;
|
||||
case action_e::help: log::println("{}", map_page_format); break;
|
||||
case action_e::none: /* ...here to run with no command */ break;
|
||||
}
|
||||
} else {
|
||||
log::println("{}", map_page_format);
|
||||
}
|
||||
if(result.count("file")){
|
||||
string path = result["file"].as<string>();
|
||||
string contents = utils::readfile(path);
|
||||
packages = utils::parse_lines(contents);
|
||||
insert<string>(in.opts, result, "file");
|
||||
}
|
||||
if(result.count("path")){
|
||||
insert<string_list>(in.opts, result, "path");
|
||||
}
|
||||
if(result.count("sort")){
|
||||
string r = result["sort"].as<string>();
|
||||
rest_api::sort_e sort = rest_api::sort_e::none;
|
||||
if(r == "none") sort = rest_api::sort_e::none;
|
||||
else if(r == "rating") sort = rest_api::sort_e::rating;
|
||||
else if(r == "cost") sort = rest_api::sort_e::cost;
|
||||
else if(r == "name") sort = rest_api::sort_e::name;
|
||||
else if(r == "updated") sort = rest_api::sort_e::updated;
|
||||
api_params.sort = sort;
|
||||
in.opts.insert(var_opt("sort", r));
|
||||
}
|
||||
if(result.count("type")){
|
||||
string r = result["type"].as<string>();
|
||||
rest_api::type_e type = rest_api::type_e::any;
|
||||
if(r == "any") type = rest_api::type_e::any;
|
||||
else if(r == "addon") type = rest_api::type_e::addon;
|
||||
else if(r == "project") type = rest_api::type_e::project;
|
||||
api_params.type = type;
|
||||
in.opts.insert(var_opt("type", r));
|
||||
}
|
||||
if(result.count("support")){
|
||||
string r = result["support"].as<string>();
|
||||
rest_api::support_e support = rest_api::support_e::all;
|
||||
if(r == "all") support = rest_api::support_e::all;
|
||||
else if(r == "official") support = rest_api::support_e::official;
|
||||
else if(r == "community") support = rest_api::support_e::community;
|
||||
else if(r == "testing") support = rest_api::support_e::testing;
|
||||
api_params.support = support;
|
||||
in.opts.insert(var_opt("support", r));
|
||||
}
|
||||
if(result.count("max-results")){
|
||||
api_params.max_results = result["max-results"].as<int>();
|
||||
insert<int>(in.opts, result, "max-results");
|
||||
}
|
||||
if(result.count("godot-version")){
|
||||
config.godot_version = result["godot-version"].as<string>();
|
||||
insert<string>(in.opts, result, "godot-version");
|
||||
}
|
||||
if(result.count("timeout")){
|
||||
config.timeout = result["timeout"].as<size_t>();
|
||||
insert<size_t>(in.opts, result, "timeout");
|
||||
}
|
||||
if(result.count("no-sync")){
|
||||
config.enable_sync = false;
|
||||
in.opts.insert(var_opt("sync", "disabled"));
|
||||
}
|
||||
if(result.count("package-dir")){
|
||||
config.packages_dir = result["package-dir"].as<string>();
|
||||
insert<string>(in.opts, result, "package-dir");
|
||||
}
|
||||
if(result.count("tmp-dir")){
|
||||
config.tmp_dir = result["tmp-dir"].as<string>();
|
||||
insert<string>(in.opts, result, "tmp-dir");
|
||||
}
|
||||
if(result.count("yes")){
|
||||
skip_prompt = true;
|
||||
in.opts.insert(opt("skip-prompt", true));
|
||||
}
|
||||
if(result.count("link")){
|
||||
packages = result["link"].as<string_list>();
|
||||
insert<string_list>(in.opts, result, "link");
|
||||
}
|
||||
if(result.count("clone")){
|
||||
packages = result["clone"].as<string_list>();
|
||||
insert<string_list>(in.opts, result, "clone");
|
||||
}
|
||||
if(result.count("clean")){
|
||||
in.opts.insert(opt("clean", true));
|
||||
clean_tmp_dir = true;
|
||||
}
|
||||
config.verbose = 0;
|
||||
config.verbose += result["verbose"].as<int>();
|
||||
insert<int>(in.opts, result, "verbose");
|
||||
|
||||
string json = to_json(config);
|
||||
if(config.verbose > 0){
|
||||
log::println("Verbose set to level {}", config.verbose);
|
||||
log::println("{}", json);
|
||||
}
|
||||
|
||||
if(!result.count("command")){
|
||||
log::error("Command required. See \"help\" for more information.");
|
||||
return result_t(in, error());
|
||||
}
|
||||
|
||||
string sub_command = result["command"].as<string>();
|
||||
if(result.count("positional")){
|
||||
string_list _argv = result["positional"].as<string_list>();
|
||||
args_t argv{_argv.begin(), _argv.end()};
|
||||
if(!argv.empty()){
|
||||
for(const auto& arg : argv){
|
||||
in.args.emplace_back(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Catch arguments passed with dashes */
|
||||
if(sub_command == "install") action = action_e::install;
|
||||
else if(sub_command == "add") action = action_e::add;
|
||||
else if(sub_command == "remove") action = action_e::remove;
|
||||
else if(sub_command == "update") action = action_e::update;
|
||||
else if(sub_command == "search") action = action_e::search;
|
||||
else if(sub_command == "export") action = action_e::p_export;
|
||||
else if(sub_command == "list") action = action_e::list;
|
||||
else if(sub_command == "link") action = action_e::link;
|
||||
else if(sub_command == "clone") action = action_e::clone;
|
||||
else if(sub_command == "clean") action = action_e::clean;
|
||||
else if(sub_command == "sync") action = action_e::sync;
|
||||
else if(sub_command == "remote") action = action_e::remote;
|
||||
else if(sub_command == "help"){ action = action_e::help;
|
||||
log::println("{}", options.help());
|
||||
}
|
||||
else{
|
||||
log::error("Unrecognized command. Try 'gdpm help' for more info.");
|
||||
}
|
||||
return result_t(in, error());
|
||||
}
|
||||
|
||||
|
||||
/* Used to run the command AFTER parsing and setting all command line args. */
|
||||
void run_command(action_e c, const var_args& args, const var_opts& opts){
|
||||
package::params params = package::make_params(args, opts);
|
||||
string_list args_list = unwrap(args);
|
||||
opts_t opts_list = unwrap(opts);
|
||||
params.skip_prompt = skip_prompt;
|
||||
switch(c){
|
||||
case action_e::install: package::install(config, args_list, params); break;
|
||||
case action_e::remove: package::remove(config, args_list, params); break;
|
||||
case action_e::update: package::update(config, args_list, params); break;
|
||||
case action_e::search: package::search(config, args_list, params); break;
|
||||
case action_e::p_export: package::export_to(args_list); break;
|
||||
case action_e::list: package::list(config, args_list, opts_list); break;
|
||||
/* ...opts are the paths here */
|
||||
case action_e::link: package::link(config, args_list, opts_list); break;
|
||||
case action_e::clone: package::clone(config, args_list, opts_list); break;
|
||||
case action_e::clean: package::clean_temporary(config, args_list); break;
|
||||
case action_e::sync: package::synchronize_database(config, args_list); break;
|
||||
case action_e::remote: remote::_handle_remote(config, args_list, opts_list); break;
|
||||
case action_e::help: /* ...runs in handle_arguments() */ break;
|
||||
case action_e::none: /* ...here to run with no command */ break;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
} // namespace gdpm::package_manager
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue