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"
1413
@@ -32,42 +31,52 @@ void DownloadService::AddDownloadTask(
3231 CLI_LOG (" Validating download items, please wait.." );
3332 // preprocess to check if all the item are valid
3433 auto total_download_size{0 };
34+ std::optional<std::string> err_msg = std::nullopt ;
3535 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 ;
36+ auto file_size = GetFileSize (item.downloadUrl );
37+ if (file_size.has_error ()) {
38+ err_msg = file_size.error ();
39+ break ;
4440 }
41+
42+ item.bytes = file_size.value ();
43+ total_download_size += file_size.value ();
44+ }
45+
46+ if (err_msg.has_value ()) {
47+ CTL_ERR (err_msg.value ());
48+ return ;
4549 }
4650
4751 // all items are valid, start downloading
48- bool download_successfully = true ;
52+ // if any item from the task failed to download, the whole task will be
53+ // considered failed
54+ std::optional<std::string> dl_err_msg = std::nullopt ;
4955 for (const auto & item : task.items ) {
5056 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 ;
57+ auto result = Download (task.id , item, true );
58+ if (result.has_error ()) {
59+ dl_err_msg = result.error ();
5660 break ;
5761 }
5862 }
63+ if (dl_err_msg.has_value ()) {
64+ CTL_ERR (dl_err_msg.value ());
65+ return ;
66+ }
5967
60- if (download_successfully && callback.has_value ()) {
68+ if (callback.has_value ()) {
6169 callback.value ()(task);
6270 }
6371}
6472
65- uint64_t DownloadService::GetFileSize (const std::string& url) const {
73+ cpp::result<uint64_t , std::string> DownloadService::GetFileSize (
74+ const std::string& url) const noexcept {
6675 CURL* curl;
6776 curl = curl_easy_init ();
6877
6978 if (!curl) {
70- throw FailedInitCurlException ( );
79+ return cpp::fail ( static_cast <std::string>( " Failed to init CURL " ) );
7180 }
7281
7382 curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L );
@@ -76,9 +85,8 @@ uint64_t DownloadService::GetFileSize(const std::string& url) const {
7685 CURLcode res = curl_easy_perform (curl);
7786
7887 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)));
88+ return cpp::fail (static_cast <std::string>(
89+ " CURL failed: " + std::string (curl_easy_strerror (res))));
8290 }
8391
8492 curl_off_t content_length = 0 ;
@@ -90,19 +98,17 @@ uint64_t DownloadService::GetFileSize(const std::string& url) const {
9098void DownloadService::AddAsyncDownloadTask (
9199 const DownloadTask& task,
92100 std::optional<OnDownloadTaskSuccessfully> callback) {
93-
101+ // just start one thread and handle all the download items
94102 for (const auto & item : task.items ) {
95103 std::thread ([this , task, &callback, item]() {
96104 this ->Download (task.id , item, false );
97105 }).detach ();
98106 }
99-
100- // TODO: how to call the callback when all the download has finished?
101107}
102108
103- void DownloadService::Download ( const std::string& download_id,
104- const DownloadItem& download_item,
105- bool allow_resume) {
109+ cpp::result< void , std::string> DownloadService::Download (
110+ const std::string& download_id, const DownloadItem& download_item,
111+ bool allow_resume) noexcept {
106112 CTL_INF (" Absolute file output: " << download_item.localPath .string ());
107113
108114 CURL* curl;
@@ -111,7 +117,7 @@ void DownloadService::Download(const std::string& download_id,
111117
112118 curl = curl_easy_init ();
113119 if (!curl) {
114- throw FailedInitCurlException ( );
120+ return cpp::fail ( static_cast <std::string>( " Failed to init CURL " ) );
115121 }
116122
117123 std::string mode = " wb" ;
@@ -121,45 +127,44 @@ void DownloadService::Download(const std::string& download_id,
121127 if (existing_file_size == -1 ) {
122128 CLI_LOG (" Cannot get file size: " << download_item.localPath .string ()
123129 << " . 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- }
143130 } 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.." );
131+ CTL_INF (" Existing file size: " << download_item.downloadUrl << " - "
132+ << download_item.localPath .string ()
133+ << " - " << existing_file_size);
134+ auto missing_bytes = download_item.bytes .value () - existing_file_size;
135+ if (missing_bytes > 0 ) {
136+ CLI_LOG (" Found unfinished download! Additional "
137+ << format_utils::BytesToHumanReadable (missing_bytes)
138+ << " need to be downloaded." );
139+ std::cout << " Continue download [Y/n]: " << std::flush;
140+ std::string answer{" " };
141+ std::getline (std::cin, answer);
142+ if (answer == " Y" || answer == " y" || answer.empty ()) {
143+ mode = " ab" ;
144+ CLI_LOG (" Resuming download.." );
145+ } else {
146+ CLI_LOG (" Start over.." );
147+ }
152148 } else {
153- return ;
149+ CLI_LOG (download_item.localPath .filename ().string ()
150+ << " is already downloaded!" );
151+ std::cout << " Re-download? [Y/n]: " << std::flush;
152+
153+ std::string answer = " " ;
154+ std::getline (std::cin, answer);
155+ if (answer == " Y" || answer == " y" || answer.empty ()) {
156+ CLI_LOG (" Re-downloading.." );
157+ } else {
158+ return {};
159+ }
154160 }
155161 }
156162 }
157163
158164 file = fopen (download_item.localPath .string ().c_str (), mode.c_str ());
159165 if (!file) {
160- auto err_msg{" Failed to open output file " +
161- download_item.localPath .string ()};
162- throw FailedOpenFileException (err_msg);
166+ return cpp::fail (" Failed to open output file " +
167+ download_item.localPath .string ());
163168 }
164169
165170 curl_easy_setopt (curl, CURLOPT_URL, download_item.downloadUrl .c_str ());
@@ -181,14 +186,13 @@ void DownloadService::Download(const std::string& download_id,
181186 res = curl_easy_perform (curl);
182187
183188 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 ());
189+ return cpp::fail (" Download failed! Error: " +
190+ static_cast <std::string>(curl_easy_strerror (res)));
188191 }
189192
190193 fclose (file);
191194 curl_easy_cleanup (curl);
195+ return {};
192196}
193197
194198curl_off_t DownloadService::GetLocalFileSize (
@@ -205,4 +209,4 @@ curl_off_t DownloadService::GetLocalFileSize(
205209 curl_off_t file_size = ftell64 (file);
206210 fclose (file);
207211 return file_size;
208- }
212+ }
0 commit comments