Recfactored and simplified more code

- Added function to convert color string to ansi color string
- Added `trim` and `join` utility functions
- Added initial plugin test case
- Implemented `config get` command to see config properties
- Improved logging functionality and removed duplicate logging functions
- Removed unused functions
- Fixed more styling issues
- Fixed some CLI commands not working correctly
- Fixed CLI documentation format
- Fixed some error handling issues
This commit is contained in:
David Allen 2023-06-18 10:47:05 -06:00
parent e48c54aa40
commit 02a4e879a8
21 changed files with 541 additions and 384 deletions

View file

@ -50,7 +50,7 @@ The project uses the CMake or Meson build system and has been tested with GCC an
* doctest (optional; for tests, but still WIP) * doctest (optional; for tests, but still WIP)
* cxxopts (header only) * clipp (header only)
* SQLite 3 * SQLite 3

View file

@ -1,3 +1,7 @@
#pragma once
#include "colors.hpp"
#include <string>
#if GDPM_ENABLE_COLORS == 1 #if GDPM_ENABLE_COLORS == 1
#define GDPM_COLOR_BLACK "\033[0;30m" #define GDPM_COLOR_BLACK "\033[0;30m"
@ -16,24 +20,26 @@
#define GDPM_COLOR_LIGHT_PURPLE "\033[0;35m" #define GDPM_COLOR_LIGHT_PURPLE "\033[0;35m"
#define GDPM_COLOR_YELLOW "\033[0;33m" #define GDPM_COLOR_YELLOW "\033[0;33m"
#define GDPM_COLOR_WHITE "\033[0;37m" #define GDPM_COLOR_WHITE "\033[0;37m"
#define GDPM_COLOR_RESET GDPM_COLOR_WHITE
#else #else
#define GDPM_COLOR_BLACK #define GDPM_COLOR_BLACK ""
#define GDPM_COLOR_BLUE #define GDPM_COLOR_BLUE ""
#define GDPM_COLOR_GREEN #define GDPM_COLOR_GREEN ""
#define GDPM_COLOR_CYAN #define GDPM_COLOR_CYAN ""
#define GDPM_COLOR_RED #define GDPM_COLOR_RED ""
#define GDPM_COLOR_PURPLE #define GDPM_COLOR_PURPLE ""
#define GDPM_COLOR_BROWN #define GDPM_COLOR_BROWN ""
#define GDPM_COLOR_GRAY #define GDPM_COLOR_GRAY ""
#define GDPM_COLOR_DARK_GRAY #define GDPM_COLOR_DARK_GRAY ""
#define GDPM_COLOR_LIGHT_BLUE #define GDPM_COLOR_LIGHT_BLUE ""
#define GDPM_COLOR_LIGHT_GREEN #define GDPM_COLOR_LIGHT_GREEN ""
#define GDPM_COLOR_LIGHT_CYAN #define GDPM_COLOR_LIGHT_CYAN ""
#define GDPM_COLOR_LIGHT_RED #define GDPM_COLOR_LIGHT_RED ""
#define GDPM_COLOR_LIGHT_PURPLE #define GDPM_COLOR_LIGHT_PURPLE ""
#define GDPM_COLOR_YELLOW #define GDPM_COLOR_YELLOW ""
#define GDPM_COLOR_WHITE #define GDPM_COLOR_WHITE ""
#define GDPM_COLOR_RESET GDPM_COLOR_WHITE
#endif #endif
@ -42,4 +48,24 @@
#define GDPM_COLOR_LOG_INFO GDPM_COLOR_LOG_RESET #define GDPM_COLOR_LOG_INFO GDPM_COLOR_LOG_RESET
#define GDPM_COLOR_LOG_ERROR GDPM_COLOR_RED #define GDPM_COLOR_LOG_ERROR GDPM_COLOR_RED
#define GDPM_COLOR_LOG_DEBUG GDPM_COLOR_YELLOW #define GDPM_COLOR_LOG_DEBUG GDPM_COLOR_YELLOW
#define GDPM_COLOR_LOG_WARNING GDPM_COLOR_YELLOW #define GDPM_COLOR_LOG_WARNING GDPM_COLOR_YELLOW
namespace gdpm::color{
inline std::string from_string(const std::string& color_name){
if (color_name == "red"){ return GDPM_COLOR_RED; }
else if (color_name == "yellow"){ return GDPM_COLOR_YELLOW; }
else if (color_name == "green"){ return GDPM_COLOR_GREEN; }
else if (color_name == "blue"){ return GDPM_COLOR_BLUE; }
else if (color_name == "brown"){ return GDPM_COLOR_BROWN; }
else if (color_name == "gray"){ return GDPM_COLOR_GRAY; }
else if (color_name == "black"){ return GDPM_COLOR_BLACK; }
else if (color_name == "purple"){ return GDPM_COLOR_PURPLE; }
else if (color_name == "gray"){ return GDPM_COLOR_DARK_GRAY; }
else if (color_name == "light-blue"){ return GDPM_COLOR_LIGHT_BLUE; }
else if (color_name == "light-green"){ return GDPM_COLOR_LIGHT_GREEN; }
else if (color_name == "light-cyan"){ return GDPM_COLOR_LIGHT_CYAN; }
else if (color_name == "light-red"){ return GDPM_COLOR_LIGHT_RED; }
else if (color_name == "light-purple"){ return GDPM_COLOR_LIGHT_PURPLE; }
return "";
}
}

View file

@ -43,7 +43,8 @@ namespace gdpm::config{
error handle_config(config::context& config, const args_t& args, const var_opts& opts); error handle_config(config::context& config, const args_t& args, const var_opts& opts);
context make_context(const string& username = GDPM_CONFIG_USERNAME, const string& password = GDPM_CONFIG_PASSWORD, const string& path = GDPM_CONFIG_PATH, const string& token = GDPM_CONFIG_TOKEN, const string& godot_version = GDPM_CONFIG_GODOT_VERSION, const string& packages_dir = GDPM_CONFIG_LOCAL_PACKAGES_DIR, const string& tmp_dir = GDPM_CONFIG_LOCAL_TMP_DIR, const string_map& remote_sources = {GDPM_CONFIG_REMOTE_SOURCES}, size_t threads = GDPM_CONFIG_THREADS, size_t timeout = 0, bool enable_sync = GDPM_CONFIG_ENABLE_SYNC, bool enable_file_logging = GDPM_CONFIG_ENABLE_FILE_LOGGING, int verbose = GDPM_CONFIG_VERBOSE); context make_context(const string& username = GDPM_CONFIG_USERNAME, const string& password = GDPM_CONFIG_PASSWORD, const string& path = GDPM_CONFIG_PATH, const string& token = GDPM_CONFIG_TOKEN, const string& godot_version = GDPM_CONFIG_GODOT_VERSION, const string& packages_dir = GDPM_CONFIG_LOCAL_PACKAGES_DIR, const string& tmp_dir = GDPM_CONFIG_LOCAL_TMP_DIR, const string_map& remote_sources = {GDPM_CONFIG_REMOTE_SOURCES}, size_t threads = GDPM_CONFIG_THREADS, size_t timeout = 0, bool enable_sync = GDPM_CONFIG_ENABLE_SYNC, bool enable_file_logging = GDPM_CONFIG_ENABLE_FILE_LOGGING, int verbose = GDPM_CONFIG_VERBOSE);
error validate(const rapidjson::Document& doc); error validate(const rapidjson::Document& doc);
void print(const context& config); void print_json(const context& config);
void print_properties(const context& config, const string_list& properties);
extern context config; extern context config;
} }

View file

@ -15,6 +15,7 @@ namespace gdpm::constants{
const std::string UserAgent("libcurl-agent/1.0 via GDPM (https://github.com/davidallendj/gdpm)"); const std::string UserAgent("libcurl-agent/1.0 via GDPM (https://github.com/davidallendj/gdpm)");
const std::string AssetRepo("https://godotengine.org/asset-library/api/asset"); const std::string AssetRepo("https://godotengine.org/asset-library/api/asset");
const std::string HostUrl("https://godotengine.org/asset-library/api"); const std::string HostUrl("https://godotengine.org/asset-library/api");
constexpr std::string WHITESPACE = " \n\r\t\f\v";
} }
/* Define default macros to set when building with -DGPM_* */ /* Define default macros to set when building with -DGPM_* */

View file

@ -29,8 +29,10 @@ namespace gdpm::constants::error{
INVALID_ARG_COUNT, INVALID_ARG_COUNT,
INVALID_CONFIG, INVALID_CONFIG,
INVALID_KEY, INVALID_KEY,
HTTP_RESPONSE_ERROR, HTTP_RESPONSE_ERR,
STD_ERROR SQLITE_ERR,
JSON_ERR,
STD_ERR
}; };
const string_list messages { const string_list messages {

View file

@ -1,10 +1,12 @@
#pragma once #pragma once
#include "clipp.h"
#include "utils.hpp" #include "utils.hpp"
#include "colors.hpp" #include "colors.hpp"
#include "types.hpp" #include "types.hpp"
#include <format> #include <format>
#include <bitset>
// #include <fmt/core.h> // #include <fmt/core.h>
#if __cplusplus > 201703L #if __cplusplus > 201703L
@ -21,22 +23,51 @@ TODO: Write log information to file
namespace gdpm::log namespace gdpm::log
{ {
enum level{ enum level_e : int{
NONE = 0, NONE = 0,
INFO, INFO = 1,
WARNING, WARNING = 2,
DEBUG, DEBUG = 3,
ERROR ERROR = 4
}; };
struct context { enum flag_opt {
int level; PRINT_STDOUT = 0b00000001,
string prefix; PRINT_STDERR = 0b00000010
string path;
bool print_to_stdout;
bool print_to_stderr;
}; };
static int level = INFO;
static string prefix = "";
static string suffix = "";
static string path = "";
static std::bitset<8> flags = PRINT_STDOUT | PRINT_STDERR;
static bool print_to_stdout;
static bool print_to_stderr;
inline constexpr level_e to_level(int l){
return static_cast<level_e>(l);
}
inline constexpr int to_int(const level_e& l){
return static_cast<int>(l);
}
inline constexpr void set_flag(uint8_t flag, bool value){
(value) ? flags.set(flag) : flags.reset(flag);
}
inline constexpr bool get_flag(uint8_t flag){
return flags.test(flag);
}
inline constexpr void set_prefix_if(const std::string& v, bool predicate = !prefix.empty()){
prefix = (predicate) ? v : prefix;
}
inline constexpr void set_suffix_if(const std::string& v, bool predicate = !suffix.empty()){
suffix = (predicate) ? v : suffix;
}
static void vlog(fmt::string_view format, fmt::format_args args){ static void vlog(fmt::string_view format, fmt::format_args args){
fmt::vprint(format, args); fmt::vprint(format, args);
} }
@ -47,30 +78,13 @@ namespace gdpm::log
template <typename S, typename...Args> template <typename S, typename...Args>
static constexpr void info(const S& format, Args&&...args){ static constexpr void info(const S& format, Args&&...args){
if(log::level < to_int(log::INFO))
return;
#if GDPM_LOG_LEVEL > NONE #if GDPM_LOG_LEVEL > NONE
set_prefix_if(fmt::format("[INFO {}] ", utils::timestamp()));
set_suffix_if("\n");
vlog( vlog(
fmt::format(GDPM_COLOR_LOG_INFO "[INFO {}] {}\n" GDPM_COLOR_LOG_RESET, utils::timestamp(), format), fmt::format(GDPM_COLOR_LOG_INFO "{}{}{}" GDPM_COLOR_LOG_RESET, prefix, format, suffix),
// fmt::make_format_args<Args...>(args...)
fmt::make_format_args(args...)
);
#endif
}
template <typename S, typename...Args>
static constexpr void info(const S& prefix, const S& format, Args&&...args){
#if GDPM_LOG_LEVEL > INFO
vlog(
fmt::format(GDPM_COLOR_LOG_INFO + prefix + GDPM_COLOR_LOG_RESET, format),
fmt::make_format_args(args...)
);
#endif
}
template <typename S, typename...Args>
static constexpr void info(const context& context, const S& format, Args&&...args){
#if GDPM_LOG_LEVEL > INFO
vlog(
fmt::format(GDPM_COLOR_LOG_INFO + context.prefix + GDPM_COLOR_LOG_RESET, format),
fmt::make_format_args(args...) fmt::make_format_args(args...)
); );
#endif #endif
@ -78,10 +92,13 @@ namespace gdpm::log
template <typename S, typename...Args> template <typename S, typename...Args>
static constexpr void info_n(const S& format, Args&&...args){ static constexpr void info_n(const S& format, Args&&...args){
if(log::level < to_int(log::INFO))
return;
#if GDPM_LOG_LEVEL > INFO #if GDPM_LOG_LEVEL > INFO
set_prefix_if(fmt::format("[INFO {}] ", utils::timestamp()));
set_suffix_if("");
vlog( vlog(
fmt::format(GDPM_COLOR_LOG_INFO "[INFO {}] {}" GDPM_COLOR_LOG_RESET, utils::timestamp(), format), fmt::format(GDPM_COLOR_LOG_INFO "{}{}{}" GDPM_COLOR_LOG_RESET, prefix, format, suffix),
// fmt::make_format_args<Args...>(args...)
fmt::make_format_args(args...) fmt::make_format_args(args...)
); );
#endif #endif
@ -89,30 +106,13 @@ namespace gdpm::log
template <typename S, typename...Args> template <typename S, typename...Args>
static constexpr void error(const S& format, Args&&...args){ static constexpr void error(const S& format, Args&&...args){
if(log::level < to_int(log::ERROR))
return;
#if GDPM_LOG_LEVEL > ERROR #if GDPM_LOG_LEVEL > ERROR
set_prefix_if(std::format("[ERROR {}] ", utils::timestamp()));
set_suffix_if("\n");
vlog( vlog(
fmt::format(GDPM_COLOR_LOG_ERROR "[ERROR {}] {}\n" GDPM_COLOR_LOG_RESET, utils::timestamp(), format), fmt::format(GDPM_COLOR_LOG_ERROR "{}{}{}" GDPM_COLOR_LOG_RESET, prefix, format, suffix),
// fmt::make_format_args<Args...>(args...)
fmt::make_format_args(args...)
);
#endif
}
template <typename S, typename...Args>
static constexpr void error(const S& prefix, const S& format, Args&&...args){
#if GDPM_LOG_LEVEL > ERROR
vlog(
fmt::format(GDPM_COLOR_LOG_ERROR + prefix + GDPM_COLOR_LOG_RESET, format),
fmt::make_format_args(args...)
);
#endif
}
template <typename S, typename...Args>
static constexpr void error(const context& context, const S& format, Args&&...args){
#if GDPM_LOG_LEVEL > ERROR
vlog(
fmt::format(GDPM_COLOR_LOG_ERROR + context.prefix + GDPM_COLOR_LOG_RESET, format),
fmt::make_format_args(args...) fmt::make_format_args(args...)
); );
#endif #endif
@ -120,30 +120,13 @@ namespace gdpm::log
template <typename S, typename...Args> template <typename S, typename...Args>
static constexpr void debug(const S& format, Args&&...args){ static constexpr void debug(const S& format, Args&&...args){
if(log::level < to_int(log::DEBUG))
return;
#if GDPM_LOG_LEVEL > DEBUG #if GDPM_LOG_LEVEL > DEBUG
set_prefix_if(std::format("[DEBUG {}] ", utils::timestamp()));
set_suffix_if("\n");
vlog( vlog(
fmt::format(GDPM_COLOR_LOG_DEBUG "[DEBUG {}] {}\n" GDPM_COLOR_LOG_RESET, utils::timestamp(), format), fmt::format(GDPM_COLOR_LOG_DEBUG "{}{}{}" GDPM_COLOR_LOG_RESET, prefix, format, suffix),
// fmt::make_format_args<Args...>(args...)
fmt::make_format_args(args...)
);
#endif
}
template <typename S, typename...Args>
static constexpr void debug(const S& prefix, const S& format, Args&&...args){
#if GDPM_LOG_LEVEL > DEBUG
vlog(
fmt::format(GDPM_COLOR_LOG_DEBUG + prefix + GDPM_COLOR_LOG_RESET, format),
fmt::make_format_args(args...)
);
#endif
}
template <typename S, typename...Args>
static constexpr void debug(const context& context, const S& format, Args&&...args){
#if GDPM_LOG_LEVEL > DEBUG
vlog(
fmt::format(GDPM_COLOR_LOG_DEBUG + context.prefix + GDPM_COLOR_LOG_RESET, format),
fmt::make_format_args(args...) fmt::make_format_args(args...)
); );
#endif #endif
@ -153,7 +136,6 @@ namespace gdpm::log
static constexpr void print(const S& format, Args&&...args){ static constexpr void print(const S& format, Args&&...args){
vlog( vlog(
fmt::format("{}", format), fmt::format("{}", format),
// fmt::make_format_args<Args...>(args...)
fmt::make_format_args(args...) fmt::make_format_args(args...)
); );
} }
@ -162,7 +144,6 @@ namespace gdpm::log
static constexpr void println(const S& format, Args&&...args){ static constexpr void println(const S& format, Args&&...args){
vlog( vlog(
fmt::format("{}\n", format), fmt::format("{}\n", format),
// fmt::make_format_args<Args...>(args...)
fmt::make_format_args(args...) fmt::make_format_args(args...)
); );
} }

View file

@ -8,6 +8,7 @@
#include "rest_api.hpp" #include "rest_api.hpp"
#include <cstdio> #include <cstdio>
#include <filesystem> #include <filesystem>
#include <functional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <rapidjson/document.h> #include <rapidjson/document.h>
@ -48,11 +49,11 @@ namespace gdpm::package {
}; };
struct params { struct params {
args_t sub_commands; args_t args;
var_opts opts; var_opts opts;
string_list paths; string_list paths;
string_list input_files; string_list input_files;
string remote_source = "origin"; string remote_source = "origin";
install_method_e install_method = GLOBAL_LINK_LOCAL; install_method_e install_method = GLOBAL_LINK_LOCAL;
}; };
@ -61,6 +62,7 @@ namespace gdpm::package {
using id_list = std::vector<size_t>; using id_list = std::vector<size_t>;
using path = std::string; using path = std::string;
using path_list = std::vector<path>; using path_list = std::vector<path>;
using path_refs = std::vector<std::reference_wrapper<const path>>;
/*! /*!
@brief Install a Godot package from the Asset Library in the current project. @brief Install a Godot package from the Asset Library in the current project.

View file

@ -16,7 +16,6 @@
#include <curl/curl.h> #include <curl/curl.h>
namespace gdpm::package_manager { namespace gdpm::package_manager {
extern remote::repository_map remote_sources;
extern CURL *curl; extern CURL *curl;
extern CURLcode res; extern CURLcode res;
extern config::context config; extern config::context config;
@ -32,10 +31,13 @@ namespace gdpm::package_manager {
link, link,
clone, clone,
clean, clean,
config, config_get,
config_set,
fetch, fetch,
sync, sync,
remote, remote_add,
remote_remove,
remote_list,
ui, ui,
help, help,
none none

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "types.hpp" #include "types.hpp"
#include <string> #include <string>
#include <filesystem>
namespace gdpm::plugin{ namespace gdpm::plugin{
struct info{ struct info{
@ -8,9 +9,11 @@ namespace gdpm::plugin{
string description; string description;
string version; string version;
}; };
extern int init(int argc, char **argv); extern error initialize(int argc, char **argv);
extern int set_name(const char *name); extern error set_name(const char *name);
extern int set_description(const char *description); extern error set_description(const char *description);
extern int set_version(const char *version); extern error set_version(const char *version);
extern int finalize(); extern error finalize();
error load(std::filesystem::path path);
} }

View file

@ -8,14 +8,8 @@
#include "config.hpp" #include "config.hpp"
namespace gdpm::remote{ namespace gdpm::remote{
using repo_names = string_list; GDPM_DLL_EXPORT error add_repository(config::context& config, const args_t& args);
using repo_urls = string_list; GDPM_DLL_EXPORT error remove_respositories(config::context& config, const args_t& names);
using repository_map = string_map; GDPM_DLL_EXPORT void move_repository(config::context& config, int old_position, int new_position);
GDPM_DLL_EXPORT void print_repositories(const config::context& config);
GDPM_DLL_EXPORT error handle_remote(config::context& config, const args_t& args, const var_opts& opts);
GDPM_DLL_EXPORT void set_repositories(config::context& context, const repository_map& repos);
GDPM_DLL_EXPORT void add_repositories(config::context& context, const repository_map& repos);
GDPM_DLL_EXPORT void remove_respositories(config::context& context, const repo_names& names);
GDPM_DLL_EXPORT void move_repository(config::context& context, int old_position, int new_position);
GDPM_DLL_EXPORT void print_repositories(const config::context& context);
} }

View file

@ -101,5 +101,4 @@ namespace gdpm{
default: /*return*/ target = 0; default: /*return*/ target = 0;
} }
} }
}
}

View file

@ -96,7 +96,12 @@ namespace gdpm::utils {
} }
std::string readfile(const std::string& path); std::string readfile(const std::string& path);
void to_lower(std::string& s); std::string to_lower(const std::string& s);
std::string trim(const std::string& s);
std::string trim_left(const std::string& s);
std::string trim_left(const std::string& s, const std::string& ref);
std::string trim_right(const std::string& s);
std::string trim_right(const std::string& s, const std::string& ref);
std::vector<std::string> parse_lines(const std::string& s); std::vector<std::string> parse_lines(const std::string& s);
std::string replace_first(std::string& s, const std::string& from, const std::string& to); std::string replace_first(std::string& s, const std::string& from, const std::string& to);
std::string replace_all(std::string& s, const std::string& from, const std::string& to); std::string replace_all(std::string& s, const std::string& from, const std::string& to);
@ -105,7 +110,7 @@ namespace gdpm::utils {
bool prompt_user_yn(const char *message); bool prompt_user_yn(const char *message);
void delay(std::chrono::milliseconds milliseconds = GDPM_REQUEST_DELAY); void delay(std::chrono::milliseconds milliseconds = GDPM_REQUEST_DELAY);
std::string join(const std::vector<std::string>& target, const std::string& delimiter = ", "); std::string join(const std::vector<std::string>& target, const std::string& delimiter = ", ");
std::string join(const std::unordered_map<std::string, std::string>& target, const std::string& prefix = "", const std::string& delimiter = "\n");
// TODO: Add function to get size of decompressed zip // TODO: Add function to get size of decompressed zip
namespace json { namespace json {

View file

View file

@ -1,5 +1,6 @@
#include "cache.hpp" #include "cache.hpp"
#include "error.hpp"
#include "log.hpp" #include "log.hpp"
#include "constants.hpp" #include "constants.hpp"
#include "package.hpp" #include "package.hpp"
@ -12,7 +13,10 @@
namespace gdpm::cache{ namespace gdpm::cache{
error create_package_database(bool overwrite, const params& params){ error create_package_database(
bool overwrite,
const params& params
){
sqlite3 *db; sqlite3 *db;
sqlite3_stmt *res; sqlite3_stmt *res;
char *errmsg; char *errmsg;
@ -77,7 +81,10 @@ namespace gdpm::cache{
} }
error insert_package_info(const package::info_list& packages, const params& params){ error insert_package_info(
const package::info_list& packages,
const params& params
){
sqlite3 *db; sqlite3 *db;
sqlite3_stmt *res; sqlite3_stmt *res;
char *errmsg = nullptr; char *errmsg = nullptr;
@ -115,7 +122,10 @@ namespace gdpm::cache{
} }
result_t<package::info_list> get_package_info_by_id(const package::id_list& package_ids, const params& params){ result_t<package::info_list> get_package_info_by_id(
const package::id_list& package_ids,
const params& params
){
sqlite3 *db; sqlite3 *db;
sqlite3_stmt *res; sqlite3_stmt *res;
char *errmsg = nullptr; char *errmsg = nullptr;
@ -179,7 +189,10 @@ namespace gdpm::cache{
} }
result_t<package::info_list> get_package_info_by_title(const package::title_list& package_titles, const params& params){ result_t<package::info_list> get_package_info_by_title(
const package::title_list& package_titles,
const params& params
){
sqlite3 *db; sqlite3 *db;
sqlite3_stmt *res; sqlite3_stmt *res;
char *errmsg = nullptr; char *errmsg = nullptr;
@ -306,17 +319,20 @@ namespace gdpm::cache{
} }
error update_package_info(const package::info_list& packages, const params& params){ error update_package_info(
const package::info_list& packages,
const params& params
){
sqlite3 *db; sqlite3 *db;
sqlite3_stmt *res; sqlite3_stmt *res;
char *errmsg = nullptr; char *errmsg = nullptr;
int rc = sqlite3_open(params.cache_path.c_str(), &db); int rc = sqlite3_open(params.cache_path.c_str(), &db);
if(rc != SQLITE_OK){ if(rc != SQLITE_OK){
error error(rc, std::format( error error(
constants::error::SQLITE_ERR, std::format(
"update_package_info.sqlite3_open(): {}", sqlite3_errmsg(db) "update_package_info.sqlite3_open(): {}", sqlite3_errmsg(db)
)); ));
log::error(error);
sqlite3_close(db); sqlite3_close(db);
return error; return error;
} }
@ -360,7 +376,10 @@ namespace gdpm::cache{
} }
error delete_packages(const package::title_list& package_titles, const params& params){ error delete_packages(
const package::title_list& package_titles,
const params& params
){
sqlite3 *db; sqlite3 *db;
sqlite3_stmt *res; sqlite3_stmt *res;
char *errmsg = nullptr; char *errmsg = nullptr;
@ -395,7 +414,10 @@ namespace gdpm::cache{
} }
error delete_packages(const package::id_list& package_ids, const params& params){ error delete_packages(
const package::id_list& package_ids,
const params& params
){
sqlite3 *db; sqlite3 *db;
sqlite3_stmt *res; sqlite3_stmt *res;
char *errmsg = nullptr; char *errmsg = nullptr;

View file

@ -37,8 +37,6 @@ namespace gdpm::config{
const context& config, const context& config,
bool pretty_print bool pretty_print
){ ){
/* Build a JSON string to pass to document */ /* Build a JSON string to pass to document */
string prefix = (pretty_print) ? "\n\t" : ""; string prefix = (pretty_print) ? "\n\t" : "";
string spaces = (pretty_print) ? " " : ""; string spaces = (pretty_print) ? " " : "";
@ -232,6 +230,7 @@ namespace gdpm::config{
return error(); return error();
} }
context make_context( context make_context(
const string& username, const string& username,
const string& password, const string& password,
@ -286,8 +285,60 @@ namespace gdpm::config{
return error; return error;
} }
void print(const context& config){ void print_json(const context& config){
log::println("{}", to_json(config, true)); log::println("{}", to_json(config, true));
} }
void _print_property(
const context& config,
const string& property
){
if(property.empty()) return;
else if(property == "username") log::println("username: {}", config.username);
else if(property == "password") log::println("password: {}", config.password);
else if(property == "path") log::println("path: {}", config.path);
else if(property == "token") log::println("token: {}", config.token);
else if(property == "packages_dir") log::println("package directory: {}", config.packages_dir);
else if(property == "tmp_dir") log::println("temporary directory: {}", config.tmp_dir);
else if(property == "remote_sources") log::println("remote sources: \n{}", utils::join(config.remote_sources, "\t", "\n"));
else if(property == "jobs") log::println("parallel jobs: {}", config.jobs);
else if(property == "timeout") log::println("timeout: {}", config.timeout);
else if(property == "sync") log::println("enable sync: {}", config.enable_sync);
else if(property == "cache") log::println("enable cache: {}", config.enable_cache);
else if(property == "prompt") log::println("skip prompt: {}", config.skip_prompt);
else if(property == "logging") log::println("enable file logging: {}", config.enable_file_logging);
else if(property == "clean") log::println("clean temporary files: {}", config.clean_temporary);
else if(property == "verbose") log::println("verbose: {}", config.verbose);
}
void print_properties(
const context& config,
const string_list& properties
){
if(properties.empty()){
_print_property(config, "username");
_print_property(config, "password");
_print_property(config, "path");
_print_property(config, "token");
_print_property(config, "packages_dir");
_print_property(config, "tmp_dir");
_print_property(config, "remote_sources");
_print_property(config, "jobs");
_print_property(config, "timeout");
_print_property(config, "sync");
_print_property(config, "cache");
_print_property(config, "prompt");
_print_property(config, "logging");
_print_property(config, "clean");
_print_property(config, "verbose");
}
std::for_each(
properties.begin(),
properties.end(),
[&config](const string& property){
_print_property(config, property);
}
);
}
} }

View file

@ -5,6 +5,7 @@
#include "config.hpp" #include "config.hpp"
#include "package_manager.hpp" #include "package_manager.hpp"
#include "result.hpp" #include "result.hpp"
#include <cstdlib>
int main(int argc, char **argv){ int main(int argc, char **argv){
@ -13,6 +14,7 @@ int main(int argc, char **argv){
error error = initialize(argc, argv); error error = initialize(argc, argv);
parse_arguments(argc, argv); parse_arguments(argc, argv);
finalize(); finalize();
return 0;
return EXIT_SUCCESS;
} }

View file

@ -8,6 +8,8 @@
#include "http.hpp" #include "http.hpp"
#include "remote.hpp" #include "remote.hpp"
#include "types.hpp" #include "types.hpp"
#include "utils.hpp"
#include <functional>
#include <future> #include <future>
#include <rapidjson/ostreamwrapper.h> #include <rapidjson/ostreamwrapper.h>
#include <rapidjson/prettywriter.h> #include <rapidjson/prettywriter.h>
@ -31,6 +33,7 @@ namespace gdpm::package{
2. Check if the package is installed. If it is, make sure it is latest version. 2. Check if the package is installed. If it is, make sure it is latest version.
If not, download and update to the latest version. If not, download and update to the latest version.
3. Extract package contents and copy/move to the correct install location. 3. Extract package contents and copy/move to the correct install location.
*/ */
result_t result = cache::get_package_info_by_title(package_titles); result_t result = cache::get_package_info_by_title(package_titles);
@ -167,7 +170,7 @@ namespace gdpm::package{
log::println("Done."); log::println("Done.");
}else{ }else{
error error( error error(
constants::error::HTTP_RESPONSE_ERROR, constants::error::HTTP_RESPONSE_ERR,
std::format("HTTP Error: {}", response.code) std::format("HTTP Error: {}", response.code)
); );
log::error(error); log::error(error);
@ -186,7 +189,12 @@ namespace gdpm::package{
/* Update the cache data with information from */ /* Update the cache data with information from */
log::info_n("Updating local asset data..."); log::info_n("Updating local asset data...");
cache::update_package_info(p_found); error error = cache::update_package_info(p_found);
if(error()){
log::error(error);
return error;
}
log::println("done."); log::println("done.");
// }) // })
// ); // );
@ -201,7 +209,8 @@ namespace gdpm::package{
const title_list& package_titles, const title_list& package_titles,
const params& params const params& params
){ ){
/* Install packages in local project instead of package database.
This will not cache the package information in the cache database. */
return error(); return error();
} }
@ -289,7 +298,13 @@ namespace gdpm::package{
} }
log::println("Done."); log::println("Done.");
log::info_n("Updating local asset data..."); log::info_n("Updating local asset data...");
cache::update_package_info(p_cache); {
error error = cache::update_package_info(p_cache);
if(error.has_occurred()){
log::error(error);
return error;
}
}
log::println("done."); log::println("done.");
return error(); return error();
@ -402,7 +417,7 @@ namespace gdpm::package{
return error; return error;
} }
log::info("{} package(s) found...", doc["total_items"].GetInt()); // log::info("{} package(s) found...", doc["total_items"].GetInt());
print_list(doc); print_list(doc);
} }
return error(); return error();
@ -416,16 +431,13 @@ namespace gdpm::package{
using namespace rapidjson; using namespace rapidjson;
using namespace std::filesystem; using namespace std::filesystem;
string show((!params.sub_commands.empty()) ? params.sub_commands[0] : ""); string show((!params.args.empty()) ? params.args[0] : "");
if(show.empty() || show == "packages"){ if(show.empty() || show == "packages"){
result_t r_installed = cache::get_installed_packages(); result_t r_installed = cache::get_installed_packages();
info_list p_installed = r_installed.unwrap_unsafe(); info_list p_installed = r_installed.unwrap_unsafe();
if(!p_installed.empty()){ if(!p_installed.empty()){
print_list(p_installed); print_list(p_installed);
} }
else{
log::println("empty");
}
} }
else if(show == "remote"){ else if(show == "remote"){
remote::print_repositories(config); remote::print_repositories(config);
@ -476,30 +488,27 @@ namespace gdpm::package{
error link( error link(
const config::context& config, const config::context& config,
const title_list& package_titles, const title_list& package_titles,
const package::params& params const package::params& params /* path is last arg */
){ ){
using namespace std::filesystem; using namespace std::filesystem;
path_list paths = {}; if(params.args.empty()){
if(params.opts.contains("path")){
paths = get<path_list>(params.opts.at("path"));
}
if(paths.empty()){
error error( error error(
constants::error::PATH_NOT_DEFINED, constants::error::INVALID_ARG_COUNT,
"No path set. Use '--path' option to set a path." "Must supply at least 2 arguments (package name and path)"
); );
log::error(error); log::error(error);
return error; return error;
} }
/* Check for packages in cache to link */
result_t r_cache = cache::get_package_info_by_title(package_titles); result_t r_cache = cache::get_package_info_by_title(package_titles);
info_list p_found = {}; info_list p_found = {};
info_list p_cache = r_cache.unwrap_unsafe(); info_list p_cache = r_cache.unwrap_unsafe();
if(p_cache.empty()){ if(p_cache.empty()){
error error( error error(
constants::error::NOT_FOUND, constants::error::NOT_FOUND,
"Could not find any packages to link." "Could not find any packages to link in cache."
); );
log::error(error); log::error(error);
return error; return error;
@ -522,20 +531,22 @@ namespace gdpm::package{
} }
/* Get the storage paths for all packages to create symlinks */ /* Get the storage paths for all packages to create symlinks */
path_refs paths = path_refs({params.args.back()});
const path package_dir{config.packages_dir}; const path package_dir{config.packages_dir};
for(const auto& p : p_found){ for(const auto& p : p_found){
for(const auto& path : paths){ for(const auto& path : paths){
log::info_n("Creating symlink for \"{}\" package to '{}'...", p.title, path + "/" + p.title); const string _path = path;
log::info_n("link: \"{}\" -> '{}'...", p.title, _path + "/" + p.title);
// std::filesystem::path target{config.packages_dir + "/" + p.title}; // std::filesystem::path target{config.packages_dir + "/" + p.title};
std::filesystem::path target = {current_path().string() + "/" + config.packages_dir + "/" + p.title}; std::filesystem::path target = {current_path().string() + "/" + config.packages_dir + "/" + p.title};
std::filesystem::path symlink_path{path + "/" + p.title}; std::filesystem::path symlink_path{_path + "/" + p.title};
if(!std::filesystem::exists(symlink_path.string())) if(!std::filesystem::exists(symlink_path.string()))
std::filesystem::create_directories(path + "/"); std::filesystem::create_directories(_path + "/");
std::error_code ec; std::error_code ec;
std::filesystem::create_directory_symlink(target, symlink_path, ec); std::filesystem::create_directory_symlink(target, symlink_path, ec);
if(ec){ if(ec){
error error( error error(
constants::error::STD_ERROR, constants::error::STD_ERR,
std::format("Could not create symlink: {}", ec.message()) std::format("Could not create symlink: {}", ec.message())
); );
log::error(error); log::error(error);
@ -554,10 +565,10 @@ namespace gdpm::package{
){ ){
using namespace std::filesystem; using namespace std::filesystem;
if(params.opts.empty()){ if(params.args.empty()){
error error( error error(
constants::error::PATH_NOT_DEFINED, constants::error::INVALID_ARG_COUNT,
"No path set. Use '--path' option to set a path." "Must supply at least 2 arguments (package name and path)"
); );
log::error(error); log::error(error);
return error; return error;
@ -565,18 +576,25 @@ namespace gdpm::package{
result_t r_cache = cache::get_package_info_by_title(package_titles); result_t r_cache = cache::get_package_info_by_title(package_titles);
package::info_list p_found = {}; package::info_list p_found = {};
package::info_list p_cache = r_cache.unwrap_unsafe(); package::info_list p_cache = r_cache.unwrap_unsafe();
/* Check for installed packages to clone */
if(p_cache.empty()){ if(p_cache.empty()){
error error( error error(
constants::error::NO_PACKAGE_FOUND, constants::error::NO_PACKAGE_FOUND,
"Could not find any packages to clone." "Could not find any packages to clone in cache."
); );
log::error(error); log::error(error);
return error; return error;
} }
for(const auto& p_title : package_titles){ for(const auto& p_title : package_titles){
auto found = std::find_if(p_cache.begin(), p_cache.end(), [&p_title](const package::info& p){ return p.title == p_title; }); auto found = std::find_if(
p_cache.begin(),
p_cache.end(),
[&p_title](const package::info& p){
return p.title == p_title;
});
if(found != p_cache.end()){ if(found != p_cache.end()){
p_found.emplace_back(*found); p_found.emplace_back(*found);
} }
@ -592,15 +610,17 @@ namespace gdpm::package{
} }
/* Get the storage paths for all packages to create clones */ /* Get the storage paths for all packages to create clones */
path_list paths = get<path_list>(params.opts.at("--path")); path_refs paths = path_refs{params.args.back()};
// path_list paths = path_list({params.args.back()});
const path package_dir{config.packages_dir}; const path package_dir{config.packages_dir};
for(const auto& p : p_found){ for(const auto& p : p_found){
for(const auto& path : paths){ for(const auto& path : paths){
log::info("Cloning \"{}\" package to {}", p.title, path + "/" + p.title); const string _path = string(path);
log::info("clone: \"{}\" -> {}", p.title, _path + "/" + p.title);
std::filesystem::path from{config.packages_dir + "/" + p.title}; std::filesystem::path from{config.packages_dir + "/" + p.title};
std::filesystem::path to{path + "/" + p.title}; std::filesystem::path to{_path + "/" + p.title};
if(!std::filesystem::exists(to.string())) if(!std::filesystem::exists(to.string()))
std::filesystem::create_directories(to); std::filesystem::create_directories(to); /* This should only occur if using a --force flag */
/* TODO: Add an option to force overwriting (i.e. --overwrite) */ /* TODO: Add an option to force overwriting (i.e. --overwrite) */
std::filesystem::copy(from, to, copy_options::update_existing | copy_options::recursive); std::filesystem::copy(from, to, copy_options::update_existing | copy_options::recursive);
@ -609,19 +629,23 @@ namespace gdpm::package{
return error(); return error();
} }
void print_list(const info_list& packages){ void print_list(const info_list& packages){
for(const auto& p : packages){ for(const auto& p : packages){
log::println("{}/{}/{} {} id={}\n\tGodot {}, {}, {}, Last Modified: {}", log::println(
GDPM_COLOR_BLUE"{}/"
GDPM_COLOR_RESET "{}/{}/{} "
GDPM_COLOR_GREEN "v{} "
GDPM_COLOR_CYAN "{} "
GDPM_COLOR_RESET "Godot {}, {}",
p.support_level, p.support_level,
p.category,
p.author, p.author,
p.title, p.title,
p.version, p.version,
p.asset_id, p.modify_date,
// p.asset_id,
p.godot_version, p.godot_version,
p.cost, p.cost
p.category,
p.modify_date
); );
} }
} }
@ -629,16 +653,22 @@ namespace gdpm::package{
void print_list(const rapidjson::Document& json){ void print_list(const rapidjson::Document& json){
for(const auto& o : json["result"].GetArray()){ for(const auto& o : json["result"].GetArray()){
log::println("{}/{}/{} {} id={}\n\tGodot {}, {}, {}, Last Modified: {}", log::println(
GDPM_COLOR_BLUE"{}/"
GDPM_COLOR_CYAN "{}/"
GDPM_COLOR_RESET "{}/{} "
GDPM_COLOR_GREEN "v{} "
GDPM_COLOR_CYAN "{} "
GDPM_COLOR_RESET "Godot {}, {}",
o["support_level"] .GetString(), o["support_level"] .GetString(),
utils::to_lower(o["category"].GetString()),
o["author"] .GetString(), o["author"] .GetString(),
o["title"] .GetString(), o["title"] .GetString(),
o["version_string"] .GetString(), o["version_string"] .GetString(),
o["asset_id"] .GetString(), o["modify_date"] .GetString(),
// o["asset_id"] .GetString(),
o["godot_version"] .GetString(), o["godot_version"] .GetString(),
o["cost"] .GetString(), o["cost"] .GetString()
o["category"] .GetString(),
o["modify_date"] .GetString()
); );
} }
} }

View file

@ -36,18 +36,16 @@
*/ */
namespace gdpm::package_manager{ namespace gdpm::package_manager{
CURL *curl; CURL *curl;
CURLcode res; CURLcode res;
config::context config; config::context config;
remote::repository_map remote_sources; action_e action;
action_e action;
// opts_t opts; // opts_t opts;
bool skip_prompt = false; bool skip_prompt = false;
bool clean_tmp_dir = false; bool clean_tmp_dir = false;
int priority = -1; int priority = -1;
error initialize(int argc, char **argv){ error initialize(int argc, char **argv){
// curl_global_init(CURL_GLOBAL_ALL); // curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init(); curl = curl_easy_init();
@ -83,166 +81,178 @@ namespace gdpm::package_manager{
error parse_arguments(int argc, char **argv){ error parse_arguments(int argc, char **argv){
using namespace clipp;
/* Replace cxxopts with clipp */ /* Replace cxxopts with clipp */
action_e action = action_e::none; action_e action = action_e::none;
package::title_list package_titles; package::title_list package_titles;
string_list input;
package::params params; package::params params;
args_t args;
var_opts opts; auto doc_format = clipp::doc_formatting{}
.first_column(7)
.doc_column(45)
.last_column(99);
/* Set global options */ /* Set global options */
auto configOpt = clipp::option("--config-path").set(config.path)% "set config path"; auto debugOpt = option("-d", "--debug").set(config.verbose, to_int(log::DEBUG)) % "show debug output";
auto fileOpt = clipp::option("--file", "-f").set(input) % "read file as input"; auto configOpt = option("--config-path").set(config.path) % "set config path";
auto pathOpt = clipp::option("--path").set(params.paths) % "specify a path to use with command"; auto fileOpt = repeatable(option("--file", "-f").set(params.args) % "read file as input");
auto typeOpt = clipp::option("--type").set(config.info.type) % "set package type (any|addon|project)"; auto pathOpt = option("--path").set(params.paths) % "specify a path to use with command";
auto sortOpt = clipp::option("--sort").set(config.api_params.sort) % "sort packages in order (rating|cost|name|updated)"; auto typeOpt = option("--type").set(config.info.type) % "set package type (any|addon|project)";
auto supportOpt = clipp::option("--support").set(config.api_params.support) % "set the support level for API (all|official|community|testing)"; auto sortOpt = option("--sort").set(config.api_params.sort) % "sort packages in order (rating|cost|name|updated)";
auto maxResultsOpt = clipp::option("--max-results").set(config.api_params.max_results) % "set the request max results"; auto supportOpt = option("--support").set(config.api_params.support) % "set the support level for API (all|official|community|testing)";
auto godotVersionOpt = clipp::option("--godot-version").set(config.api_params.godot_version) % "set the request Godot version"; auto maxResultsOpt = option("--max-results").set(config.api_params.max_results) % "set the request max results";
auto packageDirOpt = clipp::option("--package-dir").set(config.packages_dir) % "set the global package location"; auto godotVersionOpt = option("--godot-version").set(config.api_params.godot_version) % "set the request Godot version";
auto tmpDirOpt = clipp::option("--tmp-dir").set(config.tmp_dir) % "set the temporary download location"; auto packageDirOpt = option("--package-dir").set(config.packages_dir) % "set the global package location";
auto timeoutOpt = clipp::option("--timeout").set(config.timeout) % "set the request timeout"; auto tmpDirOpt = option("--tmp-dir").set(config.tmp_dir) % "set the temporary download location";
auto verboseOpt = clipp::option("--verbose", "-v").set(config.verbose) % "show verbose output"; auto timeoutOpt = option("--timeout").set(config.timeout) % "set the request timeout";
auto verboseOpt = repeatable(option("-v", "--verbose", "-v").call([]{ config.verbose += 1; })) % "show verbose output";
/* Set the options */ /* Set the options */
auto cleanOpt = clipp::option("--clean").set(config.clean_temporary) % "enable/disable cleaning temps"; auto cleanOpt = option("--clean").set(config.clean_temporary) % "enable/disable cleaning temps";
auto parallelOpt = clipp::option("--jobs").set(config.jobs) % "set number of parallel jobs"; auto parallelOpt = 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 cacheOpt = 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 syncOpt = 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 skipOpt = 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 remoteOpt = option("--remote").set(params.remote_source) % "set remote source to use";
auto packageValues = clipp::values("packages", package_titles); auto packageValues = values("packages", package_titles);
auto requiredPath = clipp::required("--path", input); auto requiredPath = required("--path", params.args);
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 installCmd = "install" % (
command("install").set(action, action_e::install),
packageValues % "packages to install from asset library",
godotVersionOpt, cleanOpt, parallelOpt, syncOpt, skipOpt, remoteOpt
); );
auto addCmd = ( auto addCmd = "add" % (
clipp::command("add").set(action, action_e::add) command("add").set(action, action_e::add),
.doc("Add a package to a local project"), packageValues % "package(s) to add to local project",
packageValues parallelOpt, skipOpt, remoteOpt
); );
auto removeCmd = ( auto removeCmd = "remove" % (
clipp::command("remove") command("remove").set(action, action_e::remove),
.set(action, action_e::remove) packageValues % "package(s) to remove from local project"
.doc("Remove a package from local project"),
packageValues
); );
auto updateCmd = ( auto updateCmd = "update" % (
clipp::command("update") command("update").set(action, action_e::update),
.set(action, action_e::update) packageValues % "update package(s)"
.doc("Update package(s)"),
packageValues
); );
auto searchCmd = ( auto searchCmd = "search" % (
clipp::command("search") command("search").set(action, action_e::search),
.set(action, action_e::search) packageValues % "package(s) to search for"
.doc("Search for package(s)"),
packageValues
); );
auto exportCmd = ( auto exportCmd = "export" % (
clipp::command("export") command("export").set(action, action_e::p_export),
.set(action, action_e::p_export) values("paths", params.args) % "export installed package list to file"
.doc("Export package list"),
clipp::values("path", input)
); );
auto listCmd = ( auto listCmd = "show installed packages" % (
clipp::command("list") command("list").set(action, action_e::list)
.set(action, action_e::list)
.doc("Show installed packages")
); );
auto linkCmd = ( auto linkCmd = "link" % (
clipp::command("link") command("link").set(action, action_e::link),
.set(action, action_e::link) value("package", package_titles) % "package name to link",
.doc("Create symlink packages to project"), value("path", params.args) % "path to project"
packageValues,
requiredPath
); );
auto cloneCmd = ( auto cloneCmd = "clone" % (
clipp::command("clone") command("clone").set(action, action_e::clone),
.set(action, action_e::clone) value("package", package_titles) % "packages to clone",
.doc("Clone packages to project"), value("path", params.args) % "path to project"
packageValues,
requiredPath
); );
auto cleanCmd = ( auto cleanCmd = "clean" % (
clipp::command("clean") command("clean").set(action, action_e::clean),
.set(action, action_e::clean) values("packages", package_titles) % "package temporary files to remove"
.doc("Clean temporary files"),
packageValues
); );
auto configCmd = ( auto configCmd = "get/set config properties" % (
clipp::command("config") command("config").set(action, action_e::config_get),
.set(action, action_e::config) (
.doc("Set/get config properties") ( greedy(command("get")).set(action, action_e::config_get),
option(repeatable(values("properties", params.args))) % "get config properties"
)
|
( command("set").set(action, action_e::config_set),
value("property", params.args[1]).call([]{}) % "config property",
value("value", params.args[2]).call([]{}) % "config value"
)
)
); );
auto fetchCmd = ( auto fetchCmd = "fetch" % (
clipp::command("fetch") command("fetch").set(action, action_e::fetch),
.set(action, action_e::fetch) option(values("remote", params.args)) % "remote to fetch asset data"
.doc("Fetch asset metadata from remote source")
); );
auto add_arg = [&params](string arg) { params.args.emplace_back(arg); };
auto remoteCmd = ( auto remoteCmd = (
clipp::command("remote") command("remote").set(action, action_e::remote_list).if_missing(
.set(action, action_e::remote) []{
.doc("Manage remote sources") remote::print_repositories(config);
.required("subcommand") }
),
(
"add a remote source" % ( command("add").set(action, action_e::remote_add),
word("name").call(add_arg) % "remote name",
value("url").call(add_arg) % "remote URL"
)
|
"remove a remote source" % ( command("remove").set(action, action_e::remote_remove),
words("names", params.args) % "remote name(s)"
)
|
"list remote sources" % ( command("list").set(action, action_e::remote_list))
)
); );
auto uiCmd = ( auto uiCmd = "start with UI" % (
clipp::command("ui") command("ui").set(action, action_e::ui)
.set(action, action_e::ui)
.doc("Show the UI")
); );
auto helpCmd = ( auto helpCmd = "show this message and exit" % (
clipp::command("help") command("help").set(action, action_e::help)
.set(action, action_e::help)
); );
auto cli = ( auto cli = (
debugOpt, configOpt,
(installCmd | addCmd | removeCmd | updateCmd | searchCmd | exportCmd | (installCmd | addCmd | removeCmd | updateCmd | searchCmd | exportCmd |
listCmd | linkCmd | cloneCmd | cleanCmd | configCmd | fetchCmd | listCmd | linkCmd | cloneCmd | cleanCmd | configCmd | fetchCmd |
remoteCmd | uiCmd | helpCmd) remoteCmd | uiCmd | helpCmd)
); );
/* Make help output */ /* Make help output */
string map_page_format(""); string man_page_format("");
auto man_page = clipp::make_man_page(cli); auto man_page = make_man_page(cli, argv[0], doc_format)
.prepend_section("DESCRIPTION", "\tManage Godot Game Engine assets from the command-line.")
.append_section("LICENSE", "\tSee the 'LICENSE.md' file for more details.");
std::for_each(man_page.begin(), man_page.end(), std::for_each(man_page.begin(), man_page.end(),
[&map_page_format](const clipp::man_page::section& s){ [&man_page_format](const man_page::section& s){
map_page_format += s.title() + "\n"; man_page_format += s.title() + "\n";
map_page_format += s.content() + "\n"; man_page_format += s.content() + "\n\n";
} }
); );
// log::level = config.verbose;
if(clipp::parse(argc, argv, cli)){ if(clipp::parse(argc, argv, cli)){
log::level = config.verbose;
switch(action){ switch(action){
case action_e::install: package::install(config, package_titles, params); break; case action_e::install: package::install(config, package_titles, params); break;
case action_e::add: break; case action_e::add: package::add(config, package_titles);
case action_e::remove: package::remove(config, package_titles, params); 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::update: package::update(config, package_titles, params); break;
case action_e::search: package::search(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::p_export: package::export_to(params.args); break;
case action_e::list: package::list(config, params); break; case action_e::list: package::list(config, params); break;
/* ...opts are the paths here */ /* ...opts are the paths here */
case action_e::link: package::link(config, package_titles, params); break; 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::clone: package::clone(config, package_titles, params); break;
case action_e::clean: package::clean_temporary(config, package_titles); 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::config_get: config::print_properties(config, params.args); break;
case action_e::fetch: package::synchronize_database(config, package_titles); break; case action_e::config_set: config::handle_config(config, package_titles, params.opts); break;
case action_e::sync: package::synchronize_database(config, package_titles); break; case action_e::fetch: package::synchronize_database(config, package_titles); break;
case action_e::remote: remote::handle_remote(config, args, opts); break; case action_e::sync: package::synchronize_database(config, package_titles); break;
case action_e::ui: log::info("ui not implemented yet"); break; case action_e::remote_list: remote::print_repositories(config); break;
case action_e::help: log::println("{}", map_page_format); break; case action_e::remote_add: remote::add_repository(config, params.args); break;
case action_e::none: /* ...here to run with no command */ break; case action_e::remote_remove: remote::remove_respositories(config, params.args); break;
case action_e::ui: log::println("UI not implemented yet"); break;
case action_e::help: log::println("{}", man_page_format); break;
case action_e::none: /* ...here to run with no command */ break;
} }
} else { } else {
log::println("{}", map_page_format); log::println("usage:\n{}", usage_lines(cli, argv[0]).str());
} }
return error(); return error();
} }

View file

@ -6,88 +6,48 @@
#include <readline/readline.h> #include <readline/readline.h>
namespace gdpm::remote{ namespace gdpm::remote{
error handle_remote(
config::context& config, error add_repository(
const args_t& args, config::context &config,
const var_opts& opts const args_t &args
){ ){
/* Check if enough arguments are supplied */ /* Check if enough args were provided. */
size_t argc = args.size(); log::debug("arg count: {}\nargs: {}", args.size(), utils::join(args));
if (argc < 1){ if (args.size() < 2){
print_repositories(config); return error(
return error(); constants::error::INVALID_ARG_COUNT,
"Requires a remote name and url argument"
);
} }
/* Check which subcommand is supplied */ /* Get the first two args */
string sub_command = args.front(); log::println("{}", args[0]);
if(sub_command == "add"){ config.remote_sources.insert({args[0], args[1]});
if(args.size() < 3 || args.empty()){
error error(
constants::error::INVALID_ARG_COUNT,
"Invalid number of args."
);
log::error(error);
return error;
}
string name = args[1];
string url = args[2];
add_repositories(config, {{name, url}});
}
else if (sub_command == "remove") {
if(args.size() < 2 || args.empty()){
error error(
constants::error::INVALID_ARG_COUNT,
"Invalid number of args."
);
log::error(error);
return error;
}
remove_respositories(config, {args.begin()+1, args.end()});
}
// else if (sub_command == "set") set_repositories(config::context &context, const repository_map &repos)
else if (sub_command == "list") print_repositories(config);
else{
error error(
constants::error::UNKNOWN,
"Unknown sub-command. Try 'gdpm help remote' for options."
);
log::error(error);
return error;
}
return error(); return error();
} }
void set_repositories( error remove_respositories(
config::context& config, config::context& config,
const repository_map &repos const args_t& args
){ ){
config.remote_sources = repos; log::debug("arg count: {}\nargs: {}", args.size(), utils::join(args));
} if(args.size() < 1){
return error(
constants::error::INVALID_ARG_COUNT,
"Requires at least one remote name argument"
);
}
for(auto it = args.begin(); it != args.end();){
void add_repositories(
config::context& config,
const repository_map &repos
){
std::for_each(repos.begin(), repos.end(),
[&config](const string_pair& p){
config.remote_sources.insert(p);
}
);
}
void remove_respositories(
config::context& config,
const repo_names& names
){
for(auto it = names.begin(); it != names.end();){
if(config.remote_sources.contains(*it)){ if(config.remote_sources.contains(*it)){
log::println("{}", *it);
config.remote_sources.erase(*it); config.remote_sources.erase(*it);
} }
it++; it++;
} }
return error();
} }

View file

@ -1,6 +1,7 @@
#include "utils.hpp" #include "utils.hpp"
#include "config.hpp" #include "config.hpp"
#include "constants.hpp"
#include "log.hpp" #include "log.hpp"
@ -13,9 +14,12 @@
#include <fcntl.h> #include <fcntl.h>
#include <rapidjson/ostreamwrapper.h> #include <rapidjson/ostreamwrapper.h>
#include <rapidjson/writer.h> #include <rapidjson/writer.h>
#include <readline/chardefs.h>
#include <readline/readline.h> #include <readline/readline.h>
#include <string>
#include <thread> #include <thread>
#include <unordered_map>
#include <zip.h> #include <zip.h>
namespace gdpm::utils{ namespace gdpm::utils{
@ -53,8 +57,38 @@ namespace gdpm::utils{
} }
#endif #endif
void to_lower(std::string& s){ std::string to_lower(const std::string& s){
std::transform(s.begin(), s.end(), s.begin(), tolower); std::string copy = s;
std::transform(copy.begin(), copy.end(), copy.begin(), tolower);
return copy;
}
std::string trim(const std::string& s){
return trim_right(trim_left(s));
}
std::string trim_left(const std::string& s){
return trim_left(s, constants::WHITESPACE);
}
std::string trim_left(
const std::string& s,
const std::string& ref
){
size_t start = s.find_first_not_of(ref);
return (start == std::string::npos) ? "" : s.substr(start);
}
std::string trim_right(const std::string& s){
return trim_right(s, constants::WHITESPACE);
}
std::string trim_right(
const std::string& s,
const std::string& ref
){
size_t end = s.find_last_not_of(ref);
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
} }
std::vector<std::string> parse_lines(const std::string &s){ std::vector<std::string> parse_lines(const std::string &s){
@ -97,7 +131,7 @@ namespace gdpm::utils{
const char *dest, const char *dest,
int verbose int verbose
){ ){
const char *prog = "gpdm"; constexpr const char *prog = "gpdm";
struct zip *za; struct zip *za;
struct zip_file *zf; struct zip_file *zf;
struct zip_stat sb; struct zip_stat sb;
@ -198,9 +232,31 @@ namespace gdpm::utils{
const std::string& delimiter const std::string& delimiter
){ ){
std::string o; std::string o;
std::for_each(target.begin(), target.end(), [&o, &delimiter](const std::string& s){ std::for_each(
o += s + delimiter; target.begin(),
}); target.end(),
[&o, &delimiter](const std::string& s){
o += s + delimiter;
}
);
o = trim_right(o, delimiter);
return o;
}
std::string join(
const std::unordered_map<std::string, std::string>& target,
const std::string& prefix,
const std::string& delimiter
){
std::string o;
std::for_each(
target.begin(),
target.end(),
[&o, &prefix, &delimiter](const std::pair<std::string, std::string>& p){
o += prefix + p.first + ": " + p.second + delimiter;
}
);
o = trim_right(o, delimiter);
return o; return o;
} }

10
tests/plugin.cpp Normal file
View file

@ -0,0 +1,10 @@
#include "plugin.hpp"
#include <doctest.h>
TEST_SUITE("Test example plugin"){
TEST_CASE("Test initialization"){
}
}