diff --git a/include/error.hpp b/include/error.hpp index 847dd12..356ae91 100644 --- a/include/error.hpp +++ b/include/error.hpp @@ -93,18 +93,17 @@ namespace gdpm{ namespace log { static constexpr void error(const gdpm::error& e){ #if GDPM_LOG_LEVEL > ERROR + set_prefix_if(std::format("[ERROR {}] ", utils::timestamp()), true); + set_suffix_if("\n"); vlog( - fmt::format(GDPM_COLOR_LOG_ERROR "[ERROR {}] {}\n" GDPM_COLOR_LOG_RESET, utils::timestamp(), e.get_message()), - fmt::make_format_args("" /*e.get_message()*/) + fmt::format(GDPM_COLOR_LOG_ERROR "{}{}\n" GDPM_COLOR_LOG_RESET, prefix.contents, e.get_message()), + fmt::make_format_args(prefix.contents, e.get_message()) ); #endif } - template - static constexpr void error(const S& prefix, const gdpm::error& e){ - vlog( - fmt::format(GDPM_COLOR_LOG_ERROR + prefix + GDPM_COLOR_LOG_RESET, e.get_message()) - ); + static void error(const char *p, const gdpm::error& e){ + println("{}{}{}", p, prefix.contents, e.get_message()); } } } diff --git a/include/log.hpp b/include/log.hpp index ac76329..07516cd 100644 --- a/include/log.hpp +++ b/include/log.hpp @@ -25,10 +25,10 @@ namespace gdpm::log enum level_e : int{ NONE = 0, - INFO = 1, - WARNING = 2, + ERROR = 1, + INFO = 2, DEBUG = 3, - ERROR = 4 + WARNING = 4, }; enum flag_opt { @@ -36,11 +36,17 @@ namespace gdpm::log PRINT_STDERR = 0b00000010 }; - static int level = INFO; - static string prefix = ""; - static string suffix = ""; - static string path = ""; - static std::bitset<8> flags = PRINT_STDOUT | PRINT_STDERR; + struct prefix { + string contents = ""; + string enclosing_start = "]"; + string enclosing_end = "["; + }; + + static int level = INFO; + static prefix 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; @@ -60,14 +66,18 @@ namespace gdpm::log 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_prefix_if(const std::string& v, bool predicate = prefix.contents.empty()){ + prefix.contents = (predicate) ? v : prefix.contents; } - inline constexpr void set_suffix_if(const std::string& v, bool predicate = !suffix.empty()){ + inline constexpr void set_suffix_if(const std::string& v, bool predicate = suffix.empty()){ suffix = (predicate) ? v : suffix; } + inline constexpr const char* get_info_prefix() { return "[INFO {}] "; } + inline constexpr const char* get_error_prefix() { return "[ERROR {}] "; } + inline constexpr const char* get_debug_prefix() { return "[DEBUG {}] "; } + static void vlog(fmt::string_view format, fmt::format_args args){ fmt::vprint(format, args); } @@ -81,10 +91,10 @@ namespace gdpm::log if(log::level < to_int(log::INFO)) return; #if GDPM_LOG_LEVEL > NONE - set_prefix_if(fmt::format("[INFO {}] ", utils::timestamp())); + set_prefix_if(fmt::format( get_info_prefix(), utils::timestamp()), true); set_suffix_if("\n"); vlog( - fmt::format(GDPM_COLOR_LOG_INFO "{}{}{}" GDPM_COLOR_LOG_RESET, prefix, format, suffix), + fmt::format(GDPM_COLOR_LOG_INFO "{}{}{}" GDPM_COLOR_LOG_RESET, prefix.contents, format, suffix), fmt::make_format_args(args...) ); #endif @@ -95,10 +105,10 @@ namespace gdpm::log if(log::level < to_int(log::INFO)) return; #if GDPM_LOG_LEVEL > INFO - set_prefix_if(fmt::format("[INFO {}] ", utils::timestamp())); - set_suffix_if(""); + set_prefix_if(fmt::format(get_info_prefix(), utils::timestamp()), true); + set_suffix_if("", true); vlog( - fmt::format(GDPM_COLOR_LOG_INFO "{}{}{}" GDPM_COLOR_LOG_RESET, prefix, format, suffix), + fmt::format(GDPM_COLOR_LOG_INFO "{}{}{}" GDPM_COLOR_LOG_RESET, prefix.contents, format, suffix), fmt::make_format_args(args...) ); #endif @@ -109,10 +119,10 @@ namespace gdpm::log if(log::level < to_int(log::ERROR)) return; #if GDPM_LOG_LEVEL > ERROR - set_prefix_if(std::format("[ERROR {}] ", utils::timestamp())); + set_prefix_if(std::format(get_error_prefix(), utils::timestamp()), true); set_suffix_if("\n"); vlog( - fmt::format(GDPM_COLOR_LOG_ERROR "{}{}{}" GDPM_COLOR_LOG_RESET, prefix, format, suffix), + fmt::format(GDPM_COLOR_LOG_ERROR "{}{}{}" GDPM_COLOR_LOG_RESET, prefix.contents, format, suffix), fmt::make_format_args(args...) ); #endif @@ -123,10 +133,10 @@ namespace gdpm::log if(log::level < to_int(log::DEBUG)) return; #if GDPM_LOG_LEVEL > DEBUG - set_prefix_if(std::format("[DEBUG {}] ", utils::timestamp())); + set_prefix_if(std::format(get_debug_prefix(), utils::timestamp()), true); set_suffix_if("\n"); vlog( - fmt::format(GDPM_COLOR_LOG_DEBUG "{}{}{}" GDPM_COLOR_LOG_RESET, prefix, format, suffix), + fmt::format(GDPM_COLOR_LOG_DEBUG "{}{}{}" GDPM_COLOR_LOG_RESET, prefix.contents, format, suffix), fmt::make_format_args(args...) ); #endif diff --git a/include/package_manager.hpp b/include/package_manager.hpp index a552390..e195fa8 100644 --- a/include/package_manager.hpp +++ b/include/package_manager.hpp @@ -40,6 +40,7 @@ namespace gdpm::package_manager { remote_list, ui, help, + version, none }; diff --git a/include/utils.hpp b/include/utils.hpp index 838f5ca..66b09f7 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -103,8 +103,8 @@ namespace gdpm::utils { std::string trim_right(const std::string& s); std::string trim_right(const std::string& s, const std::string& ref); std::vector parse_lines(const std::string& s); - 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_first(const std::string& s, const std::string& from, const std::string& to); + std::string replace_all(const std::string& s, const std::string& from, const std::string& to); int extract_zip(const char *archive, const char *dest, int verbose = 0); std::string prompt_user(const char *message); bool prompt_user_yn(const char *message); diff --git a/src/cache.cpp b/src/cache.cpp index 45b8f1a..2a40f6d 100644 --- a/src/cache.cpp +++ b/src/cache.cpp @@ -13,6 +13,10 @@ namespace gdpm::cache{ + std::string _escape_sql(const std::string& s){ + return utils::replace_all(s, "'", "''"); + } + error create_package_database( bool overwrite, const params& params @@ -31,13 +35,12 @@ namespace gdpm::cache{ int rc = sqlite3_open(params.cache_path.c_str(), &db); if(rc != SQLITE_OK){ - error error(rc, + error error(constants::error::SQLITE_ERR, std::format( "create_package_database.sqlite3_open(): {}", sqlite3_errmsg(db) ) ); - log::error(error); sqlite3_close(db); return error; } @@ -67,11 +70,10 @@ namespace gdpm::cache{ rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errmsg); if(rc != SQLITE_OK){ // log::error("Failed to fetch data: {}\n", sqlite3_errmsg(db)); - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "create_package_database.sqlite3_exec(): {}", errmsg )); - log::error(error); sqlite3_free(errmsg); sqlite3_close(db); return error; @@ -99,11 +101,10 @@ namespace gdpm::cache{ // log::println("{}", sql); int rc = sqlite3_open(params.cache_path.c_str(), &db); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "insert_package_info.sqlite3_open(): {}", sqlite3_errmsg(db) )); - log::error(error); sqlite3_close(db); return error; } @@ -162,10 +163,9 @@ namespace gdpm::cache{ int rc = sqlite3_open(params.cache_path.c_str(), &db); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "get_package_info_by_id.sqlite3_open(): {}", sqlite3_errmsg(db) )); - log::error(error); sqlite3_close(db); return result_t(package::info_list(), error); } @@ -176,10 +176,9 @@ namespace gdpm::cache{ sql += "COMMIT;\n"; rc = sqlite3_exec(db, sql.c_str(), callback, (void*)&p_vector, &errmsg); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "get_package_info_by_id.sqlite3_exec(): {}", errmsg )); - log::error("get_package_info_by_id.sqlite3_exec(): {}", errmsg); sqlite3_free(errmsg); sqlite3_close(db); return result_t(package::info_list(), error); @@ -232,10 +231,9 @@ namespace gdpm::cache{ int rc = sqlite3_open(params.cache_path.c_str(), &db); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "get_package_info_by_title.sqlite3_open(): {}", sqlite3_errmsg(db) )); - log::error(error); sqlite3_close(db); return result_t(package::info_list(), error); } @@ -248,10 +246,9 @@ namespace gdpm::cache{ // log::println(sql); rc = sqlite3_exec(db, sql.c_str(), callback, (void*)&p_vector, &errmsg); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "get_package_info_by_title.sqlite3_exec(): {}", errmsg )); - log::error(error); sqlite3_free(errmsg); sqlite3_close(db); return result_t(package::info_list(), error); @@ -295,10 +292,9 @@ namespace gdpm::cache{ int rc = sqlite3_open(params.cache_path.c_str(), &db); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "get_installed_packages.sqlite3_open(): {}", sqlite3_errmsg(db) )); - log::error(error); sqlite3_close(db); return result_t(package::info_list(), error); } @@ -339,34 +335,33 @@ namespace gdpm::cache{ string sql; for(const auto& p : packages){ - sql += "UPDATE " + params.table_name + " SET " - " asset_id=" + std::to_string(p.asset_id) + ", " - " type='" + p.type + "', " - " title='" + p.title + "', " - " author='" + p.author + "', " + - " author_id=" + std::to_string(p.author_id) + ", " - " version='" + p.version + "', " + - " godot_version='" + p.godot_version + "', " + - " cost='" + p.cost + "', " + - " description='" + p.description + "', " + - " modify_date='" + p.modify_date + "', " + - " support_level='" + p.support_level + "', " + - " category='" + p.category + "', " + - " remote_source='" + p.remote_source + "', " + - " download_url='" + p.download_url + "', " + - " download_hash='" + p.download_hash + "', " + - " is_installed=" + std::to_string(p.is_installed) + ", " - " install_path='" + p.install_path + "'" + sql += "UPDATE " + params.table_name + " SET " + " asset_id=" + std::to_string(p.asset_id) + ", " + " type='" + _escape_sql(p.type) + "', " + " title='" + _escape_sql(p.title) + "', " + " author='" + _escape_sql(p.author) + "', " + + " author_id=" + std::to_string(p.author_id) + ", " + " version='" + _escape_sql(p.version) + "', " + + " godot_version='" + _escape_sql(p.godot_version) + "', " + + " cost='" + _escape_sql(p.cost) + "', " + + " description='" + _escape_sql(p.description) + "', " + + " modify_date='" + _escape_sql(p.modify_date) + "', " + + " support_level='" + _escape_sql(p.support_level) + "', " + + " category='" + _escape_sql(p.category) + "', " + + " remote_source='" + _escape_sql(p.remote_source) + "', " + + " download_url='" + _escape_sql(p.download_url) + "', " + + " download_hash='" + _escape_sql(p.download_hash) + "', " + + " is_installed=" + std::to_string(p.is_installed) + ", " + " install_path='" + _escape_sql(p.install_path) + "'" // " dependencies='" + p.dependencies + "'" - " WHERE title='" + p.title + "' AND asset_id=" + std::to_string(p.asset_id) + " WHERE title='" + p.title + "' AND asset_id=" + std::to_string(p.asset_id) + ";\n"; } rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errmsg); if(rc != SQLITE_OK){ - error error(rc, std::format( - "update_package_info.sqlite3_exec(): {}", errmsg + error error(constants::error::SQLITE_ERR, std::format( + "update_package_info.sqlite3_exec(): {}\n\t{}", errmsg, sql )); - log::error(error); sqlite3_free(errmsg); sqlite3_close(db); return error; @@ -387,10 +382,9 @@ namespace gdpm::cache{ int rc = sqlite3_open(params.cache_path.c_str(), &db); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "delete_packages.sqlite3_open(): {}", sqlite3_errmsg(db) )); - log::error(error); sqlite3_close(db); return error; } @@ -401,10 +395,9 @@ namespace gdpm::cache{ } rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errmsg); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "delete_packages.sqlite3_exec(): {}", errmsg )); - log::error(error); sqlite3_free(errmsg); sqlite3_close(db); return error; @@ -425,10 +418,9 @@ namespace gdpm::cache{ int rc = sqlite3_open(params.cache_path.c_str(), &db); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "delete_packages.sqlite3_open(): {}", errmsg )); - log::error(error); sqlite3_close(db); return error; } @@ -439,10 +431,9 @@ namespace gdpm::cache{ } rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errmsg); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "delete_packages.sqlite3_exec(): {}", errmsg )); - log::error(error); sqlite3_free(errmsg); sqlite3_close(db); return error; @@ -460,20 +451,18 @@ namespace gdpm::cache{ int rc = sqlite3_open(params.cache_path.c_str(), &db); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "drop_package_database.sqlite3_open(): {}", sqlite3_errmsg(db) )); - log::error(error); sqlite3_close(db); return error; } rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errmsg); if(rc != SQLITE_OK){ - error error(rc, std::format( + error error(constants::error::SQLITE_ERR, std::format( "drop_package_database.sqlite3_exec(): {}", errmsg )); - log::error(error); sqlite3_free(errmsg); sqlite3_close(db); return error; diff --git a/src/package.cpp b/src/package.cpp index 89dd36f..0dfc982 100644 --- a/src/package.cpp +++ b/src/package.cpp @@ -1,5 +1,6 @@ #include "package.hpp" +#include "colors.hpp" #include "error.hpp" #include "log.hpp" #include "rest_api.hpp" @@ -11,6 +12,7 @@ #include "utils.hpp" #include #include +#include #include #include @@ -89,7 +91,7 @@ namespace gdpm::package{ log::println(""); if(!config.skip_prompt){ - if(!utils::prompt_user_yn("Do you want to install these packages? (y/n)")) + if(!utils::prompt_user_yn("Do you want to install these packages? (Y/n)")) return error(); } @@ -118,13 +120,14 @@ namespace gdpm::package{ /* Retrieve necessary asset data if it was found already in cache */ Document doc; - bool is_valid = p.download_url.empty() || p.category.empty() || p.description.empty() || p.support_level.empty(); - if(is_valid){ + bool is_missing_data = p.download_url.empty() || p.category.empty() || p.description.empty() || p.support_level.empty(); + if(is_missing_data){ doc = rest_api::get_asset(url, p.asset_id, rest_api_params); if(doc.HasParseError() || doc.IsNull()){ - constexpr const char *message = "\nError parsing HTTP response."; + const std::string message = std::format("Error parsing JSON: {}", GetParseError_En(doc.GetParseError())); log::error(message); - return error(doc.GetParseError(), message); + + return error(constants::error::JSON_ERR, std::format("{}: {}", message, GetParseError_En(doc.GetParseError()))); } p.category = doc["category"].GetString(); p.description = doc["description"].GetString(); @@ -133,7 +136,7 @@ namespace gdpm::package{ p.download_hash = doc["download_hash"].GetString(); } else{ - log::error("Not a valid package."); + log::info("Found asset data found for \"{}\"", p.title); } /* Set directory and temp paths for storage */ @@ -162,10 +165,8 @@ namespace gdpm::package{ } else{ /* Download all the package files and place them in tmp directory. */ - log::info_n("Downloading \"{}\"...", p.title); - std::string download_url = p.download_url;// doc["download_url"].GetString(); - std::string title = p.title;// doc["title"].GetString(); - http::response response = http::download_file(download_url, tmp_zip); + log::info("Downloading \"{}\"...", p.title); + http::response response = http::download_file(p.download_url, tmp_zip); if(response.code == http::OK){ log::println("Done."); }else{ @@ -190,12 +191,13 @@ namespace gdpm::package{ /* Update the cache data with information from */ log::info_n("Updating local asset data..."); error error = cache::update_package_info(p_found); - if(error()){ - log::error(error); + if(error.has_occurred()){ + string prefix = std::format(log::get_error_prefix(), utils::timestamp()); + log::println(GDPM_COLOR_LOG_ERROR"\n{}{}" GDPM_COLOR_RESET, prefix, error.get_message()); return error; } - log::println("done."); + log::println("Done."); // }) // ); } @@ -301,11 +303,11 @@ namespace gdpm::package{ { error error = cache::update_package_info(p_cache); if(error.has_occurred()){ - log::error(error); + log::error("\n{}", error.get_message()); return error; } } - log::println("done."); + log::println("Done."); return error(); } diff --git a/src/package_manager.cpp b/src/package_manager.cpp index 7305c00..6b8b387 100644 --- a/src/package_manager.cpp +++ b/src/package_manager.cpp @@ -106,8 +106,9 @@ namespace gdpm::package_manager{ auto packageDirOpt = option("--package-dir").set(config.packages_dir) % "set the global package location"; auto tmpDirOpt = option("--tmp-dir").set(config.tmp_dir) % "set the temporary download location"; 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"; - + auto verboseOpt = joinable(repeatable(option("-v", "--verbose").call([]{ config.verbose += 1; }))) % "show verbose output"; + auto versionOpt = option("--version").set(action, action_e::version); + /* Set the options */ auto cleanOpt = option("--clean").set(config.clean_temporary) % "enable/disable cleaning temps"; auto parallelOpt = option("--jobs").set(config.jobs) % "set number of parallel jobs"; @@ -207,7 +208,7 @@ namespace gdpm::package_manager{ ); auto cli = ( - debugOpt, configOpt, + debugOpt, configOpt, verboseOpt, versionOpt, (installCmd | addCmd | removeCmd | updateCmd | searchCmd | exportCmd | listCmd | linkCmd | cloneCmd | cleanCmd | configCmd | fetchCmd | remoteCmd | uiCmd | helpCmd) @@ -249,6 +250,7 @@ namespace gdpm::package_manager{ 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::version: break; case action_e::none: /* ...here to run with no command */ break; } } else { diff --git a/src/rest_api.cpp b/src/rest_api.cpp index 0150322..9426c1b 100644 --- a/src/rest_api.cpp +++ b/src/rest_api.cpp @@ -18,8 +18,9 @@ namespace gdpm::rest_api{ request_params make_from_config(const config::context& config){ - request_params params = make_request_params(); - params.godot_version = config.info.godot_version; + bool is_latest = (config.info.godot_version.empty() || config.info.godot_version == "latest"); + request_params params = make_request_params(); + params.godot_version = (is_latest) ? "" : config.info.godot_version; params.verbose = config.verbose; return params; } @@ -37,7 +38,7 @@ namespace gdpm::rest_api{ bool reverse, int verbose ){ - request_params params{ + return request_params{ .type = type, .category = category, .support = support, @@ -50,7 +51,6 @@ namespace gdpm::rest_api{ .reverse = reverse, .verbose = verbose }; - return params; } bool register_account( @@ -211,7 +211,7 @@ namespace gdpm::rest_api{ string request_url = _prepare_request(url, c); http::response r = http::request_get(request_url); if(c.verbose > 0) - log::info("URL: {}", request_url); + log::info("get_asset().URL: {}", request_url); return _parse_json(r.body, c.verbose); } @@ -220,11 +220,10 @@ namespace gdpm::rest_api{ int asset_id, const request_params& params ){ - string request_url = _prepare_request(url, params); - utils::replace_all(request_url, "{id}", std::to_string(asset_id)); + string request_url = utils::replace_all(_prepare_request(url, params), "{id}", std::to_string(asset_id)); http::response r = http::request_get(request_url.c_str()); - if(params.verbose > 0) - log::info("URL: {}", request_url); + if(params.verbose >= log::INFO) + log::info("get_asset().URL: {}", request_url); return _parse_json(r.body); } diff --git a/src/utils.cpp b/src/utils.cpp index efcb1df..31b90e9 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -102,27 +102,29 @@ namespace gdpm::utils{ } std::string replace_first( - std::string &s, + const std::string &s, const std::string &from, const std::string &to ){ - size_t pos = s.find(from); + std::string copy = s; // make string copy + size_t pos = copy.find(from); if(pos == std::string::npos) - return s; - return s.replace(pos, from.length(), to); + return copy; + return copy.replace(pos, from.length(), to); } std::string replace_all( - std::string& s, + const std::string& s, const std::string& from, const std::string& to ){ + std::string copy = s; // make string copy size_t pos = 0; - while((pos = s.find(from, pos)) != std::string::npos){ - s.replace(pos, s.length(), to); + while((pos = copy.find(from, pos)) != std::string::npos){ + copy.replace(pos, s.length(), to); pos += to.length(); } - return s; + return copy; } /* Ref: https://gist.github.com/mobius/1759816 */ @@ -204,17 +206,18 @@ namespace gdpm::utils{ std::string prompt_user(const char *message){ log::print("{} ", message); std::string input; - std::cin >> input; + // std::cin >> input; + getline(std::cin, input); return input; } bool prompt_user_yn(const char *message){ - std::string input{"y"}; - do{ - input = utils::prompt_user(message); - to_lower(input); - input = (input == "\0" || input == "\n" || input.empty()) ? "y" : input; - }while( !std::cin.fail() && input != "y" && input != "n"); + std::string input{""}; + while( !std::cin.fail() && input != "y" && input != "n" ){ + bool is_default = (input == "\0" || input == "\n" || input == "\r\n" || input.empty()); + input = to_lower(utils::prompt_user(message)); + input = is_default ? "y" : input; + } return input == "y"; }