mirror of
https://github.com/davidallendj/gdpm.git
synced 2025-12-20 03:27:02 -07:00
Implemented parallel downloads through CURL multi interface and added purge command
- Update `README.md` file with examples - Fixed error messages showing no or wrong message - Changed the prompt message when installing, removing, etc. - Changed how http::request works - Added `http::multi` class for parallel downloads - Removed separate `concepts.hpp` file TODO: Fix ZIP not extracting after running the `install` command
This commit is contained in:
parent
766eabd5b2
commit
807aa8e5b2
21 changed files with 1158 additions and 758 deletions
|
|
@ -1,6 +0,0 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace gdpm::concepts{
|
||||
template <typename...Args> concept RequireMinArgs = requires (std::size_t min){ sizeof...(Args) > min; };
|
||||
}
|
||||
|
|
@ -36,8 +36,8 @@ namespace gdpm::print{
|
|||
#define GDPM_CONFIG_REMOTE_SOURCES std::pair<std::string, std::string>(constants::RemoteName, constants::HostUrl)
|
||||
#define GDPM_CONFIG_THREADS 1
|
||||
#define GDPM_CONFIG_TIMEOUT_MS 30000
|
||||
#define GDPM_CONFIG_ENABLE_SYNC 1
|
||||
#define GDPM_CONFIG_ENABLE_FILE_LOGGING 0
|
||||
#define GDPM_CONFIG_ENABLE_SYNC true
|
||||
#define GDPM_CONFIG_ENABLE_FILE_LOGGING true
|
||||
#define GDPM_CONFIG_VERBOSE 0
|
||||
|
||||
/* Defines the default package cache for local storage */
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "log.hpp"
|
||||
#include "types.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <fmt/core.h>
|
||||
#include <new>
|
||||
#include <string>
|
||||
|
|
@ -15,11 +16,14 @@ namespace gdpm::constants::error{
|
|||
UNKNOWN_COMMAND,
|
||||
UNKNOWN_ARGUMENT,
|
||||
ARGPARSE_ERROR,
|
||||
ASSERTION_FAILED,
|
||||
PRECONDITION_FAILED,
|
||||
POSTCONDITION_FAILED,
|
||||
NOT_FOUND,
|
||||
NOT_DEFINED,
|
||||
NOT_IMPLEMENTED,
|
||||
NO_PACKAGE_FOUND,
|
||||
PATH_NOT_DEFINED,
|
||||
MALFORMED_PATH,
|
||||
FILE_EXISTS,
|
||||
FILE_NOT_FOUND,
|
||||
DIRECTORY_EXISTS,
|
||||
|
|
@ -43,12 +47,17 @@ namespace gdpm::constants::error{
|
|||
"",
|
||||
"An unknown error has occurred.",
|
||||
"Unknown command.",
|
||||
"Unknown argument.",
|
||||
"Could not parse argument.",
|
||||
"Assertion condition failed.",
|
||||
"Pre-condition failed.",
|
||||
"Post-condition failed.",
|
||||
"Resource not found.",
|
||||
"Function not defined.",
|
||||
"Function not implemented.",
|
||||
"Resource not defined.",
|
||||
"Resource not implemented.",
|
||||
"No package found.",
|
||||
"Path is not well-defined",
|
||||
"File found.",
|
||||
"Path is malformed.",
|
||||
"File already exists",
|
||||
"File does not exist.",
|
||||
"Directory exists.",
|
||||
"Directory not found.",
|
||||
|
|
@ -58,6 +67,10 @@ namespace gdpm::constants::error{
|
|||
"Invalid configuration.",
|
||||
"Invalid key.",
|
||||
"An HTTP response error has occurred.",
|
||||
"A SQLite error has occurred.",
|
||||
"A libzip error has occurred.",
|
||||
"A libcurl error has occurred.",
|
||||
"A JSON error has occurred.",
|
||||
"An error has occurred."
|
||||
};
|
||||
|
||||
|
|
@ -73,10 +86,12 @@ namespace gdpm::constants::error{
|
|||
};
|
||||
|
||||
namespace gdpm{
|
||||
namespace ec = constants::error;
|
||||
class error {
|
||||
public:
|
||||
constexpr explicit error(int code = 0, const string& message = "{code}"):
|
||||
m_code(code), m_message(message == "{code}" ? constants::error::get_message(code): message)
|
||||
constexpr explicit error(int code = 0, const string& message = "{default}"):
|
||||
m_code(code),
|
||||
m_message(utils::replace_all(message, "{default}", ec::get_message(code)))
|
||||
{}
|
||||
|
||||
void set_code(int code) { m_code = code; }
|
||||
|
|
@ -106,13 +121,21 @@ namespace gdpm{
|
|||
#endif
|
||||
}
|
||||
|
||||
// static constexpr void error(int code, const string& message = "{default}"){
|
||||
// log::error(gdpm::error(code, message));
|
||||
// }
|
||||
|
||||
static constexpr gdpm::error error_rc(const gdpm::error& e){
|
||||
error(e);
|
||||
log::error(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
static void error(const char *p, const gdpm::error& e){
|
||||
println("{}{}{}", p, prefix.contents, e.get_message());
|
||||
static constexpr gdpm::error error_rc(int code, const string& message = "{default}"){
|
||||
return error_rc(gdpm::error(code, message));
|
||||
}
|
||||
}
|
||||
|
||||
namespace concepts{
|
||||
template <typename T>concept error_t = requires{ std::is_same<error, T>::value; };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
#include "constants.hpp"
|
||||
#include "types.hpp"
|
||||
#include "indicators/indeterminate_progress_bar.hpp"
|
||||
#include "indicators/dynamic_progress.hpp"
|
||||
#include "indicators/progress_bar.hpp"
|
||||
#include "indicators/block_progress_bar.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <curl/curl.h>
|
||||
#include <curl/easy.h>
|
||||
|
|
@ -10,6 +16,13 @@ namespace gdpm::http{
|
|||
using headers_t = std::unordered_map<string, string>;
|
||||
using header = std::pair<string, string>;
|
||||
|
||||
enum method{
|
||||
GET,
|
||||
POST,
|
||||
PUT,
|
||||
DELETE
|
||||
};
|
||||
|
||||
// REF: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
|
||||
enum response_code{
|
||||
CONTINUE = 100,
|
||||
|
|
@ -77,6 +90,11 @@ namespace gdpm::http{
|
|||
NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
};
|
||||
|
||||
enum transfer_type{
|
||||
REQUEST,
|
||||
DOWNLOAD
|
||||
};
|
||||
|
||||
struct response{
|
||||
long code = 0;
|
||||
string body{};
|
||||
|
|
@ -85,31 +103,91 @@ namespace gdpm::http{
|
|||
};
|
||||
|
||||
|
||||
struct request_params {
|
||||
struct request {
|
||||
headers_t headers = {};
|
||||
method method = method::GET;
|
||||
size_t timeout = GDPM_CONFIG_TIMEOUT_MS;
|
||||
int verbose = 0;
|
||||
};
|
||||
|
||||
using namespace indicators;
|
||||
// BlockProgressBar bar {
|
||||
// option::BarWidth{50},
|
||||
// // option::Start{"["},
|
||||
// // option::Fill{"="},
|
||||
// // option::Lead{">"},
|
||||
// // option::Remainder{" "},
|
||||
// // option::End{"]"},
|
||||
// option::PrefixText{"Downloading file "},
|
||||
// option::PostfixText{""},
|
||||
// option::ForegroundColor{Color::green},
|
||||
// option::FontStyles{std::vector<FontStyle>{FontStyle::bold}},
|
||||
// };
|
||||
// // option::ShowElapsedTime{true},
|
||||
// // option::ShowRemainingTime{true},
|
||||
// IndeterminateProgressBar bar_unknown {
|
||||
// option::BarWidth{50},
|
||||
// option::Start{"["},
|
||||
// option::Fill{"."},
|
||||
// option::Lead{"<==>"},
|
||||
// option::PrefixText{"Downloading file "},
|
||||
// option::End{"]"},
|
||||
// option::PostfixText{""},
|
||||
// option::ForegroundColor{Color::green},
|
||||
// option::FontStyles{std::vector<FontStyle>{FontStyle::bold}},
|
||||
// };
|
||||
|
||||
struct transfer : public non_copyable{
|
||||
transfer(){ curl = curl_easy_init(); }
|
||||
transfer(transfer&&){}
|
||||
~transfer(){ }
|
||||
|
||||
CURLcode res;
|
||||
int id;
|
||||
CURL *curl = nullptr;
|
||||
FILE *fp = nullptr;
|
||||
utils::memory_buffer data = {0};
|
||||
|
||||
};
|
||||
using transfers = std::vector<transfer>;
|
||||
using responses = std::vector<response>;
|
||||
|
||||
class context : public non_copyable{
|
||||
public:
|
||||
context();
|
||||
~context();
|
||||
|
||||
inline CURL* const get_curl() const;
|
||||
string url_escape(const string& url);
|
||||
response request_get(const string& url, const http::request_params& params = http::request_params());
|
||||
response request_post(const string& url, const http::request_params& params = http::request_params());
|
||||
response download_file(const string& url, const string& storage_path, const http::request_params& params = http::request_params());
|
||||
response request(const string& url, const http::request& params = http::request());
|
||||
response download_file(const string& url, const string& storage_path, const http::request& params = http::request());
|
||||
long get_download_size(const string& url);
|
||||
long get_bytes_downloaded(const string& url);
|
||||
|
||||
|
||||
private:
|
||||
CURL *curl;
|
||||
curl_slist* _add_headers(CURL *curl, const headers_t& headers);
|
||||
CURL *curl = nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
extern context http;
|
||||
class multi{
|
||||
public:
|
||||
multi(long max_allowed_transfers = 2);
|
||||
~multi();
|
||||
string url_escape(const string& url);
|
||||
ptr<transfers> make_requests(const string_list& urls, const http::request& params = http::request());
|
||||
ptr<transfers> make_downloads(const string_list& url, const string_list& storage_path, const http::request& params = http::request());
|
||||
ptr<responses> execute(ptr<transfers> transfers, size_t timeout = 1000);
|
||||
|
||||
private:
|
||||
DynamicProgress<BlockProgressBar> progress_bars;
|
||||
CURLM *cm = nullptr;
|
||||
CURLMsg *cmessage = nullptr;
|
||||
CURLMcode cres;
|
||||
int messages_left = -1;
|
||||
};
|
||||
|
||||
curl_slist* add_headers(CURL *curl, const headers_t& headers);
|
||||
static size_t write_to_buffer(char *contents, size_t size, size_t nmemb, void *userdata);
|
||||
static size_t write_to_stream(char *ptr, size_t size, size_t nmemb, void *userdata);
|
||||
static int show_download_progress(void *ptr, curl_off_t total_download, curl_off_t current_downloaded, curl_off_t total_upload, curl_off_t current_upload);
|
||||
|
||||
}
|
||||
|
|
@ -110,8 +110,11 @@ namespace gdpm::package {
|
|||
GDPM_DLL_EXPORT error search(const config::context& config, const title_list& package_titles, const params& params = package::params());
|
||||
GDPM_DLL_EXPORT error list(const config::context& config, const params& params = package::params());
|
||||
GDPM_DLL_EXPORT error export_to(const path_list& paths);
|
||||
GDPM_DLL_EXPORT error clean(const config::context& config, const title_list& package_titles);
|
||||
GDPM_DLL_EXPORT error purge(const config::context& config);
|
||||
GDPM_DLL_EXPORT error link(const config::context& config, const title_list& package_titles, const params& params = package::params());
|
||||
GDPM_DLL_EXPORT error clone(const config::context& config, const title_list& package_titles, const params& params = package::params());
|
||||
GDPM_DLL_EXPORT result_t<info_list> fetch(const config::context& config, const title_list& package_titles);
|
||||
|
||||
|
||||
GDPM_DLL_EXPORT void print_list(const rapidjson::Document& json);
|
||||
|
|
@ -120,12 +123,10 @@ namespace gdpm::package {
|
|||
GDPM_DLL_EXPORT void print_table(const rapidjson::Document& json);
|
||||
GDPM_DLL_EXPORT result_t<info_list> get_package_info(const opts_t& opts);
|
||||
GDPM_DLL_EXPORT result_t<title_list> get_package_titles(const info_list& packages);
|
||||
GDPM_DLL_EXPORT void clean_temporary(const config::context& config, const title_list& package_titles);
|
||||
GDPM_DLL_EXPORT void read_file_inputs(title_list& package_titles, const path_list& paths);
|
||||
GDPM_DLL_EXPORT info_list find_cached_packages(const title_list& package_titles);
|
||||
GDPM_DLL_EXPORT info_list find_installed_packages(const title_list& package_titles);
|
||||
/* Dependency Management API */
|
||||
GDPM_DLL_EXPORT result_t<info_list> synchronize_database(const config::context& config, const title_list& package_titles);
|
||||
GDPM_DLL_EXPORT result_t<info_list> resolve_dependencies(const config::context& config, const title_list& package_titles);
|
||||
|
||||
GDPM_DLL_EXPORT string to_json(const info& info, bool pretty_print = false);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ namespace gdpm::package_manager {
|
|||
update,
|
||||
search,
|
||||
p_export, /* reserved keyword */
|
||||
purge,
|
||||
list,
|
||||
link,
|
||||
clone,
|
||||
|
|
|
|||
|
|
@ -63,25 +63,29 @@ namespace gdpm::rest_api{
|
|||
};
|
||||
|
||||
request_params make_from_config(const config::context& config);
|
||||
string to_json(const rapidjson::Document& doc);
|
||||
string to_json(const json::document& doc);
|
||||
string to_string(type_e type);
|
||||
string to_string(support_e support);
|
||||
string to_string(sort_e sort);
|
||||
error print_params(const request_params& params, const string& filter = "");
|
||||
error print_asset(const request_params& params, const string& filter = "", const print::style& style = print::style::list);
|
||||
rapidjson::Document _parse_json(const string& r, int verbose = 0);
|
||||
json::document _parse_json(const string& r, int verbose = 0);
|
||||
string _prepare_request(const string& url, const request_params& context, const string& filter);
|
||||
|
||||
bool register_account(const string& username, const string& password, const string& email);
|
||||
bool login(const string& username, const string& password);
|
||||
bool logout();
|
||||
|
||||
rapidjson::Document configure(const string& url = constants::HostUrl, type_e type = any, int verbose = 0);
|
||||
rapidjson::Document get_assets_list(const string& url, const request_params& params = {}, const string& filter = "");
|
||||
rapidjson::Document get_asset(const string& url, int asset_id, const request_params& params = {}, const string& filter = "");
|
||||
json::document configure(const string& url = constants::HostUrl, type_e type = any, int verbose = 0);
|
||||
json::document get_assets_list(const string& url, const request_params& params = {}, const string& filter = "");
|
||||
json::document get_asset(const string& url, int asset_id, const request_params& params = {}, const string& filter = "");
|
||||
bool delete_asset(int asset_id); // ...for moderators
|
||||
bool undelete_asset(int asset_id); // ...for moderators
|
||||
bool set_support_level(int asset_id); // ...for moderators
|
||||
|
||||
namespace multi{
|
||||
json::documents get_assets(const string_list& urls, id_list aset_ids, const request_params& api_params, const string_list& filters);
|
||||
}
|
||||
|
||||
/*
|
||||
POST /asset
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@
|
|||
|
||||
#include "log.hpp"
|
||||
#include "error.hpp"
|
||||
#include "types.hpp"
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace gdpm{
|
||||
|
||||
template <class T, error_t U = error>
|
||||
template <class T, concepts::error_t U = error>
|
||||
class result_t {
|
||||
public:
|
||||
result_t() = delete;
|
||||
|
|
@ -35,13 +34,14 @@ namespace gdpm{
|
|||
fn_error = error;
|
||||
}
|
||||
|
||||
constexpr U get_error() const{
|
||||
return std::get<U>(data);
|
||||
}
|
||||
|
||||
constexpr std::unique_ptr<T> unwrap() const {
|
||||
/* First, check if ok() and error() are defined. */
|
||||
if(!fn_error || !fn_ok){
|
||||
error error(
|
||||
constants::error::NOT_DEFINED
|
||||
);
|
||||
log::error(error);
|
||||
log::error(error(ec::NOT_DEFINED));
|
||||
return nullptr;
|
||||
}
|
||||
/* Then, attempt unwrap the data. */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
|
@ -41,13 +42,11 @@ namespace gdpm{
|
|||
SIZE_T = 6,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept error_t = requires{ std::is_same<error, T>::value; };
|
||||
|
||||
using string = std::string;
|
||||
using string_list = std::vector<string>;
|
||||
using string_map = std::unordered_map<string, string>;
|
||||
using string_pair = std::pair<string, string>;
|
||||
using id_list = std::vector<int>;
|
||||
using any = std::any;
|
||||
using var = std::variant<int, float, bool, string, string_list, string_map, size_t>;
|
||||
template <typename T = var>
|
||||
|
|
@ -65,6 +64,15 @@ namespace gdpm{
|
|||
template <typename T = error>
|
||||
using _task_list = std::vector<std::future<T>>;
|
||||
using task_list = _task_list<error>;
|
||||
template <typename T>
|
||||
using ptr = std::unique_ptr<T>;
|
||||
namespace json{
|
||||
using document = rapidjson::Document;
|
||||
using documents = std::vector<rapidjson::Document>;
|
||||
}
|
||||
namespace concepts{
|
||||
template <typename...Args> concept require_min_args = requires (std::size_t min){ sizeof...(Args) > min; };
|
||||
}
|
||||
|
||||
inline string_list unwrap(const var_args& args){
|
||||
string_list sl;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "constants.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
|
@ -71,6 +72,11 @@ namespace gdpm::utils {
|
|||
std::move(part, from.end(), std::back_inserter(from));
|
||||
from.erase(part);
|
||||
}
|
||||
template <class T>
|
||||
std::vector<T> append(const std::vector<T>& a, const std::vector<T>& b){
|
||||
a.insert(std::end(a), std::begin(b), std::end(b));
|
||||
return a;
|
||||
}
|
||||
|
||||
bool to_bool(const std::string& s);
|
||||
std::vector<std::string> split_lines(const std::string& contents);
|
||||
|
|
@ -84,7 +90,7 @@ namespace gdpm::utils {
|
|||
std::vector<std::string> parse_lines(const std::string& s);
|
||||
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);
|
||||
error 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);
|
||||
void delay(std::chrono::milliseconds milliseconds = GDPM_REQUEST_DELAY);
|
||||
|
|
@ -93,11 +99,6 @@ namespace gdpm::utils {
|
|||
std::string convert_size(long size);
|
||||
// TODO: Add function to get size of decompressed zip
|
||||
|
||||
namespace curl {
|
||||
extern size_t write_to_buffer(char *contents, size_t size, size_t nmemb, void *userdata);
|
||||
extern size_t write_to_stream(char *ptr, size_t size, size_t nmemb, void *userdata);
|
||||
extern int show_progress(void *ptr, curl_off_t total_download, curl_off_t current_downloaded, curl_off_t total_upload, curl_off_t current_upload);
|
||||
}
|
||||
namespace json {
|
||||
std::string from_array(const std::set<std::string>& a, const std::string& prefix);
|
||||
std::string from_object(const std::unordered_map<std::string, std::string>& m, const std::string& prefix, const std::string& spaces);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue