Fixed minor issues

- Fixed error message formatting
- Fixed setting verbosity levels
- Fixed implementation of `replace_first` and `replace_all`
- Fixed issue where installing a package would failed because of unescaped single quotes in SQL statement
- Fixed error code returned from SQLITE related errors
- Fixed verbose flag not being repeatable and set correctly
- Fixed some error messages to correctly reflect occuring error
- Fixed the prompt asking the user twice and not taking a default input
- Added functions to get logging default prefixes
- Added `version` command (not implemented yet)
This commit is contained in:
David Allen 2023-06-18 21:10:50 -06:00
parent 02a4e879a8
commit c88cb08e03
9 changed files with 126 additions and 121 deletions

View file

@ -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 <typename S, typename...Args>
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());
}
}
}

View file

@ -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

View file

@ -40,6 +40,7 @@ namespace gdpm::package_manager {
remote_list,
ui,
help,
version,
none
};

View file

@ -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<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_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);

View file

@ -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;

View file

@ -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 <functional>
#include <future>
#include <rapidjson/error/en.h>
#include <rapidjson/ostreamwrapper.h>
#include <rapidjson/prettywriter.h>
@ -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();
}

View file

@ -106,7 +106,8 @@ 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";
@ -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 {

View file

@ -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);
}

View file

@ -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";
}