#include "package_manager.hpp" #include "error.hpp" #include "package.hpp" #include "utils.hpp" #include "rest_api.hpp" #include "remote.hpp" #include "config.hpp" #include "constants.hpp" #include "log.hpp" #include "http.hpp" #include "cache.hpp" #include "types.hpp" #include #include #include #include #include #include #include #include #include #include #include #include /* * For cURLpp examples...see the link below: * * https://github.com/jpbarrette/curlpp/tree/master/examples */ 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; // opts_t opts; bool skip_prompt = false; bool clean_tmp_dir = false; int priority = -1; result_t 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; /* Check for config and create if not exists */ if(!std::filesystem::exists(config.path)){ config::save(config.path, config); } error error = config::load(config.path, config); if(error.has_occurred()){ log::error(error); } /* Create the local databases if it doesn't exist already */ error = cache::create_package_database(); if(error.has_occurred()){ log::error(error); } /* Run the rest of the program then exit */ cxxargs args = _parse_arguments(argc, argv); return _handle_arguments(args); } 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(){ curl_easy_cleanup(curl); error error = config::save(config.path, config); if(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()) ("positional", "", cxxopts::value()) ("install", "Install package or packages.", cxxopts::value()->implicit_value(""), "") ("remove", "Remove a package or packages.", cxxopts::value()->implicit_value(""), "") ("update", "Update a package or packages. This will update all packages if no argument is provided.", cxxopts::value()->implicit_value(""), "") ("search", "Search for a package or packages.", cxxopts::value(), "") ("export", "Export list of packages", cxxopts::value()->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(), "") ("clone", "Clone packages into target directory. Must be used with the `--path` argument.", cxxopts::value(), "") ("cache", "Caching operations", cxxopts::value()) ("clean", "Clean temporary downloaded files.") ("fetch", "Fetch asset data from remote sources.") ("remote", "Set a source repository.", cxxopts::value()->default_value(constants::AssetRepo), "") ("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()) ("f,file", "Read file to install or remove packages.", cxxopts::value(), "") ("path", "Specify a path to use with a command", cxxopts::value()) ("type", "Set package type (any|addon|project).", cxxopts::value()) ("sort", "Sort packages in order (rating|cost|name|updated).", cxxopts::value()) ("support", "Set the support level for API (all|official|community|testing).") ("max-results", "Set the max results to return from search.", cxxopts::value()->default_value("500"), "") ("godot-version", "Set the Godot version to include in request.", cxxopts::value()) ("package-dir", "Set the local package storage location.", cxxopts::value()) ("tmp-dir", "Set the local temporary storage location.", cxxopts::value()) ("timeout", "Set the amount of time to wait for a response.", cxxopts::value()) ("no-sync", "Disable synchronizing with remote.", cxxopts::value()->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()->implicit_value("1")->default_value("0"), "0-5") ; auto result = options.parse(argc, argv); return {result, options}; } template void insert( var_opts& opts, const cxxopts::ParseResult& result, const string& key ){ opts.insert(var_opt(key, result[key].as())); }; result_t _handle_arguments(const cxxargs& args){ const auto& result = args.result; const auto& options = args.options; exec_args in; // auto get_opt = [](const string& key){ // result[key].as(); // } // auto get_opt = [&result](const string& key){ // return opt(key, result[key].as()); // }; /* Set option variables first to be used in functions below. */ if(result.count("search")){ in.args = result["search"].as(); } if(result.count("help")){ log::println("{}", options.help()); } if(result.count("config")){ config.path = result["config"].as(); insert(in.opts, result, "config"); config::load(config.path, config); log::info("Config: {}", config.path); } if(result.count("remote")){ string sub_command = result["remote"].as(); log::print("sub command: {}", sub_command); if(sub_command == "add"){ string argv = result.arguments_string(); log::println("argv: {}", argv); remote::add_repositories(config, {}); } else if(sub_command == "remove"){ remote::remove_respositories(config, {}); log::println("argv: {}"); } } if(result.count("file")){ string path = result["file"].as(); string contents = utils::readfile(path); packages = utils::parse_lines(contents); insert(in.opts, result, "file"); } if(result.count("path")){ insert(in.opts, result, "path"); } if(result.count("sort")){ string r = result["sort"].as(); 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(); 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(); 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(); insert(in.opts, result, "max-results"); } if(result.count("godot-version")){ config.godot_version = result["godot-version"].as(); insert(in.opts, result, "godot-version"); } if(result.count("timeout")){ config.timeout = result["timeout"].as(); insert(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(); insert(in.opts, result, "package-dir"); } if(result.count("tmp-dir")){ config.tmp_dir = result["tmp-dir"].as(); insert(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(); insert(in.opts, result, "link"); } if(result.count("clone")){ packages = result["clone"].as(); insert(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(); insert(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(); if(result.count("positional")){ string_list _argv = result["positional"].as(); 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; } } } // namespace gdpm::package_manager