44#include < stdio.h>
55#include < trantor/utils/Logger.h>
66#include < filesystem>
7+ #include < optional>
78#include < ostream>
89#include < thread>
9- #include " exceptions/failed_curl_exception.h"
10- #include " exceptions/failed_init_curl_exception.h"
11- #include " exceptions/failed_open_file_exception.h"
10+ #include " download_service.h"
1211#include " utils/format_utils.h"
1312#include " utils/logging_utils.h"
13+ #include " utils/result.hpp"
1414
1515#ifdef _WIN32
1616#define ftell64 (f ) _ftelli64(f)
@@ -27,47 +27,58 @@ size_t WriteCallback(void* ptr, size_t size, size_t nmemb, FILE* stream) {
2727}
2828} // namespace
2929
30- void DownloadService::AddDownloadTask (
30+ cpp::result< void , std::string> DownloadService::AddDownloadTask (
3131 DownloadTask& task, std::optional<OnDownloadTaskSuccessfully> callback) {
3232 CLI_LOG (" Validating download items, please wait.." );
3333 // preprocess to check if all the item are valid
3434 auto total_download_size{0 };
35+ std::optional<std::string> err_msg = std::nullopt ;
3536 for (auto & item : task.items ) {
36- try {
37- auto size = GetFileSize (item.downloadUrl );
38- item.bytes = size;
39- total_download_size += size;
40- } catch (const FailedCurlException& e) {
41- CTL_ERR (" Found invalid download item: " << item.downloadUrl << " - "
42- << e.what ());
43- throw ;
37+ auto file_size = GetFileSize (item.downloadUrl );
38+ if (file_size.has_error ()) {
39+ err_msg = file_size.error ();
40+ break ;
4441 }
42+
43+ item.bytes = file_size.value ();
44+ total_download_size += file_size.value ();
45+ }
46+
47+ if (err_msg.has_value ()) {
48+ CTL_ERR (err_msg.value ());
49+ return cpp::fail (err_msg.value ());
4550 }
4651
4752 // all items are valid, start downloading
48- bool download_successfully = true ;
53+ // if any item from the task failed to download, the whole task will be
54+ // considered failed
55+ std::optional<std::string> dl_err_msg = std::nullopt ;
4956 for (const auto & item : task.items ) {
5057 CLI_LOG (" Start downloading: " + item.localPath .filename ().string ());
51- try {
52- Download (task.id , item, true );
53- } catch (const std::runtime_error& e) {
54- CTL_ERR (" Failed to download: " << item.downloadUrl << " - " << e.what ());
55- download_successfully = false ;
58+ auto result = Download (task.id , item, true );
59+ if (result.has_error ()) {
60+ dl_err_msg = result.error ();
5661 break ;
5762 }
5863 }
64+ if (dl_err_msg.has_value ()) {
65+ CTL_ERR (dl_err_msg.value ());
66+ return cpp::fail (dl_err_msg.value ());
67+ }
5968
60- if (download_successfully && callback.has_value ()) {
69+ if (callback.has_value ()) {
6170 callback.value ()(task);
6271 }
72+ return {};
6373}
6474
65- uint64_t DownloadService::GetFileSize (const std::string& url) const {
75+ cpp::result<uint64_t , std::string> DownloadService::GetFileSize (
76+ const std::string& url) const noexcept {
6677 CURL* curl;
6778 curl = curl_easy_init ();
6879
6980 if (!curl) {
70- throw FailedInitCurlException ( );
81+ return cpp::fail ( static_cast <std::string>( " Failed to init CURL " ) );
7182 }
7283
7384 curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L );
@@ -76,9 +87,8 @@ uint64_t DownloadService::GetFileSize(const std::string& url) const {
7687 CURLcode res = curl_easy_perform (curl);
7788
7889 if (res != CURLE_OK) {
79- // if we have a failed here. it meant the url is invalid
80- throw FailedCurlException (" CURL failed: " +
81- std::string (curl_easy_strerror (res)));
90+ return cpp::fail (static_cast <std::string>(
91+ " CURL failed: " + std::string (curl_easy_strerror (res))));
8292 }
8393
8494 curl_off_t content_length = 0 ;
@@ -90,19 +100,17 @@ uint64_t DownloadService::GetFileSize(const std::string& url) const {
90100void DownloadService::AddAsyncDownloadTask (
91101 const DownloadTask& task,
92102 std::optional<OnDownloadTaskSuccessfully> callback) {
93-
103+ // just start one thread and handle all the download items
94104 for (const auto & item : task.items ) {
95105 std::thread ([this , task, &callback, item]() {
96106 this ->Download (task.id , item, false );
97107 }).detach ();
98108 }
99-
100- // TODO: how to call the callback when all the download has finished?
101109}
102110
103- void DownloadService::Download ( const std::string& download_id,
104- const DownloadItem& download_item,
105- bool allow_resume) {
111+ cpp::result< void , std::string> DownloadService::Download (
112+ const std::string& download_id, const DownloadItem& download_item,
113+ bool allow_resume) noexcept {
106114 CTL_INF (" Absolute file output: " << download_item.localPath .string ());
107115
108116 CURL* curl;
@@ -111,7 +119,7 @@ void DownloadService::Download(const std::string& download_id,
111119
112120 curl = curl_easy_init ();
113121 if (!curl) {
114- throw FailedInitCurlException ( );
122+ return cpp::fail ( static_cast <std::string>( " Failed to init CURL " ) );
115123 }
116124
117125 std::string mode = " wb" ;
@@ -121,45 +129,44 @@ void DownloadService::Download(const std::string& download_id,
121129 if (existing_file_size == -1 ) {
122130 CLI_LOG (" Cannot get file size: " << download_item.localPath .string ()
123131 << " . Start download over!" );
124- return ;
125- }
126- CTL_INF (" Existing file size: " << download_item.downloadUrl << " - "
127- << download_item.localPath .string () << " - "
128- << existing_file_size);
129- auto missing_bytes = download_item.bytes .value () - existing_file_size;
130- if (missing_bytes > 0 ) {
131- CLI_LOG (" Found unfinished download! Additional "
132- << format_utils::BytesToHumanReadable (missing_bytes)
133- << " need to be downloaded." );
134- std::cout << " Continue download [Y/n]: " << std::flush;
135- std::string answer{" " };
136- std::getline (std::cin, answer);
137- if (answer == " Y" || answer == " y" || answer.empty ()) {
138- mode = " ab" ;
139- CLI_LOG (" Resuming download.." );
140- } else {
141- CLI_LOG (" Start over.." );
142- }
143132 } else {
144- CLI_LOG (download_item.localPath .filename ().string ()
145- << " is already downloaded!" );
146- std::cout << " Re-download? [Y/n]: " << std::flush;
147-
148- std::string answer = " " ;
149- std::getline (std::cin, answer);
150- if (answer == " Y" || answer == " y" || answer.empty ()) {
151- CLI_LOG (" Re-downloading.." );
133+ CTL_INF (" Existing file size: " << download_item.downloadUrl << " - "
134+ << download_item.localPath .string ()
135+ << " - " << existing_file_size);
136+ auto missing_bytes = download_item.bytes .value () - existing_file_size;
137+ if (missing_bytes > 0 ) {
138+ CLI_LOG (" Found unfinished download! Additional "
139+ << format_utils::BytesToHumanReadable (missing_bytes)
140+ << " need to be downloaded." );
141+ std::cout << " Continue download [Y/n]: " << std::flush;
142+ std::string answer{" " };
143+ std::getline (std::cin, answer);
144+ if (answer == " Y" || answer == " y" || answer.empty ()) {
145+ mode = " ab" ;
146+ CLI_LOG (" Resuming download.." );
147+ } else {
148+ CLI_LOG (" Start over.." );
149+ }
152150 } else {
153- return ;
151+ CLI_LOG (download_item.localPath .filename ().string ()
152+ << " is already downloaded!" );
153+ std::cout << " Re-download? [Y/n]: " << std::flush;
154+
155+ std::string answer = " " ;
156+ std::getline (std::cin, answer);
157+ if (answer == " Y" || answer == " y" || answer.empty ()) {
158+ CLI_LOG (" Re-downloading.." );
159+ } else {
160+ return {};
161+ }
154162 }
155163 }
156164 }
157165
158166 file = fopen (download_item.localPath .string ().c_str (), mode.c_str ());
159167 if (!file) {
160- auto err_msg{" Failed to open output file " +
161- download_item.localPath .string ()};
162- throw FailedOpenFileException (err_msg);
168+ return cpp::fail (" Failed to open output file " +
169+ download_item.localPath .string ());
163170 }
164171
165172 curl_easy_setopt (curl, CURLOPT_URL, download_item.downloadUrl .c_str ());
@@ -181,14 +188,13 @@ void DownloadService::Download(const std::string& download_id,
181188 res = curl_easy_perform (curl);
182189
183190 if (res != CURLE_OK) {
184- fprintf (stderr, " curl_easy_perform() failed: %s\n " ,
185- curl_easy_strerror (res));
186- throw std::runtime_error (" Failed to download file " +
187- download_item.localPath .filename ().string ());
191+ return cpp::fail (" Download failed! Error: " +
192+ static_cast <std::string>(curl_easy_strerror (res)));
188193 }
189194
190195 fclose (file);
191196 curl_easy_cleanup (curl);
197+ return {};
192198}
193199
194200curl_off_t DownloadService::GetLocalFileSize (
@@ -205,4 +211,4 @@ curl_off_t DownloadService::GetLocalFileSize(
205211 curl_off_t file_size = ftell64 (file);
206212 fclose (file);
207213 return file_size;
208- }
214+ }
0 commit comments