Skip to content

Commit 5c86dfb

Browse files
Merge pull request #183 from code0-tech/159-cli-download-failed
[CLI] Retry Download
2 parents ef64cfd + 1f5ad59 commit 5c86dfb

File tree

1 file changed

+120
-81
lines changed

1 file changed

+120
-81
lines changed

crates/cli/src/command/download.rs

Lines changed: 120 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::formatter::{error_without_trace, info, success};
22
use bytes::Bytes;
3+
use log::error;
34
use reqwest::header::{ACCEPT, USER_AGENT};
5+
use reqwest::{Client, Response};
46
use serde::Deserialize;
57
use std::fs;
68
use std::fs::File;
@@ -17,7 +19,6 @@ struct Release {
1719
struct Asset {
1820
name: String,
1921
browser_download_url: String,
20-
size: u64,
2122
}
2223

2324
pub async fn handle_download(tag: Option<String>, features: Option<Vec<String>>) {
@@ -26,13 +27,7 @@ pub async fn handle_download(tag: Option<String>, features: Option<Vec<String>>)
2627

2728
// Download the definitions
2829
info("Starting download process...".to_string());
29-
let bytes = match download_definitions_as_bytes(tag).await {
30-
Some(bytes) => bytes,
31-
None => {
32-
error_without_trace(String::from("Download failed."));
33-
return;
34-
}
35-
};
30+
let bytes = download_definitions_as_bytes(tag).await;
3631

3732
// Extract the zip file
3833
convert_bytes_to_folder(bytes, zip_path).await;
@@ -52,7 +47,8 @@ pub async fn handle_download(tag: Option<String>, features: Option<Vec<String>>)
5247
));
5348
}
5449

55-
async fn download_definitions_as_bytes(tag: Option<String>) -> Option<bytes::Bytes> {
50+
async fn download_definitions_as_bytes(tag: Option<String>) -> Bytes {
51+
let max_retries = 3;
5652
let client = reqwest::Client::new();
5753

5854
let url = match tag {
@@ -66,23 +62,46 @@ async fn download_definitions_as_bytes(tag: Option<String>) -> Option<bytes::Byt
6662
}
6763
};
6864

69-
let release_request = match client
70-
.get(url)
71-
.header(USER_AGENT, "code0-definition-cli")
72-
.header(ACCEPT, "application/vnd.github+json")
73-
.send()
74-
.await
75-
{
76-
Ok(response) => {
77-
if response.status().is_success() {
78-
response
79-
} else {
80-
return None;
81-
}
65+
async fn download_release(client: &Client, url: String) -> Response {
66+
match client
67+
.get(url)
68+
.header(USER_AGENT, "code0-definition-cli")
69+
.header(ACCEPT, "application/vnd.github+json")
70+
.send()
71+
.await
72+
{
73+
Ok(r) => r,
74+
Err(e) => panic!("Request failed: {:?}", e),
8275
}
83-
Err(e) => {
84-
panic!("Request failed: {}", e);
76+
}
77+
78+
let mut retries = 0;
79+
let mut result = None;
80+
let mut succeeded = false;
81+
82+
while !succeeded {
83+
let release_request = download_release(&client, url.clone()).await;
84+
if release_request.status().is_success() {
85+
succeeded = true;
86+
result = Some(release_request);
87+
} else {
88+
if retries >= max_retries {
89+
panic!("Reached max retries while downloading release.")
90+
}
91+
92+
retries += 1;
93+
error!(
94+
"Retrying ({}/{}) download. Failed with status code: {:?}",
95+
retries,
96+
max_retries,
97+
release_request.status()
98+
);
8599
}
100+
}
101+
102+
let release_request = match result {
103+
Some(r) => r,
104+
None => panic!("Failed to download release"),
86105
};
87106

88107
let release: Release = match release_request.json::<Release>().await {
@@ -91,7 +110,7 @@ async fn download_definitions_as_bytes(tag: Option<String>) -> Option<bytes::Byt
91110
release
92111
}
93112
Err(e) => {
94-
panic!("Request failed: {}", e);
113+
panic!("Request failed: {:?}", e);
95114
}
96115
};
97116

@@ -108,34 +127,53 @@ async fn download_definitions_as_bytes(tag: Option<String>) -> Option<bytes::Byt
108127
}
109128
};
110129

111-
match client
112-
.get(&asset.browser_download_url)
113-
.header(USER_AGENT, "code0-definition-cli")
114-
.send()
115-
.await
116-
{
117-
Ok(response) => {
118-
if response.status().is_success() {
119-
match response.bytes().await {
120-
Ok(bytes) => {
121-
info("Download completed successfully".to_string());
122-
Some(bytes)
123-
}
124-
Err(e) => {
125-
error_without_trace(format!("Failed to read download data: {e}"));
126-
None
127-
}
128-
}
129-
} else {
130-
error_without_trace(format!(
131-
"Download failed with status: {}",
132-
response.status()
133-
));
134-
None
130+
let mut asset_retries = 0;
131+
let mut asset_result = None;
132+
let mut asset_success = false;
133+
134+
while !asset_success {
135+
let response = match client
136+
.get(&asset.browser_download_url)
137+
.header(USER_AGENT, "code0-definition-cli")
138+
.send()
139+
.await
140+
{
141+
Ok(response) => response,
142+
Err(e) => {
143+
panic!("Download request failed: {:?}", e);
144+
}
145+
};
146+
147+
if response.status().is_success() {
148+
asset_success = true;
149+
asset_result = Some(response);
150+
} else {
151+
if asset_retries >= max_retries {
152+
panic!("Reached max retries while downloading asset!");
135153
}
154+
155+
asset_retries += 1;
156+
error!(
157+
"Retrying ({}/{}) asset download. Failed with status code: {:?}",
158+
asset_retries,
159+
max_retries,
160+
response.status()
161+
);
162+
}
163+
}
164+
165+
let response = match asset_result {
166+
Some(r) => r,
167+
None => panic!("Failed to download asset!"),
168+
};
169+
170+
match response.bytes().await {
171+
Ok(bytes) => {
172+
info("Download completed successfully".to_string());
173+
bytes
136174
}
137175
Err(e) => {
138-
panic!("Download request failed: {e}");
176+
panic!("Failed to read downloaded data: {:?}", e);
139177
}
140178
}
141179
}
@@ -148,14 +186,14 @@ async fn convert_bytes_to_folder(bytes: Bytes, zip_path: &str) {
148186
let zip_file = match File::open(zip_path) {
149187
Ok(file) => file,
150188
Err(e) => {
151-
panic!("Failed to open zip file: {e}");
189+
panic!("Failed to open zip file: {:?}", e);
152190
}
153191
};
154192

155193
let mut archive = match ZipArchive::new(zip_file) {
156194
Ok(archive) => archive,
157195
Err(e) => {
158-
panic!("Failed to read zip archive: {e}");
196+
panic!("Failed to read zip archive: {:?}", e);
159197
}
160198
};
161199

@@ -166,7 +204,7 @@ async fn convert_bytes_to_folder(bytes: Bytes, zip_path: &str) {
166204
let mut file = match archive.by_index(i) {
167205
Ok(file) => file,
168206
Err(e) => {
169-
panic!("Failed to read file at index {i}: {e}");
207+
panic!("Failed to read file at index {i}: {:?}", e);
170208
}
171209
};
172210

@@ -177,15 +215,15 @@ async fn convert_bytes_to_folder(bytes: Bytes, zip_path: &str) {
177215

178216
if file.name().ends_with('/') {
179217
if let Err(e) = fs::create_dir_all(&out_path) {
180-
panic!("Failed to create directory {}: {}", out_path.display(), e);
218+
panic!("Failed to create directory {}: {:?}", out_path.display(), e);
181219
}
182220
} else {
183221
if let Some(p) = out_path.parent()
184222
&& !p.exists()
185223
&& let Err(e) = fs::create_dir_all(p)
186224
{
187225
panic!(
188-
"Warning: Failed to create parent directory {}: {}",
226+
"Warning: Failed to create parent directory {}: {:?}",
189227
p.display(),
190228
e
191229
);
@@ -194,11 +232,11 @@ async fn convert_bytes_to_folder(bytes: Bytes, zip_path: &str) {
194232
match File::create(&out_path) {
195233
Ok(mut outfile) => {
196234
if let Err(e) = std::io::copy(&mut file, &mut outfile) {
197-
panic!("Warning: Failed to extract {}: {}", out_path.display(), e);
235+
panic!("Warning: Failed to extract {}: {:?}", out_path.display(), e);
198236
}
199237
}
200238
Err(e) => {
201-
panic!("Failed to create file {}: {}", out_path.display(), e);
239+
panic!("Failed to create file {}: {:?}", out_path.display(), e);
202240
}
203241
}
204242
}
@@ -209,40 +247,41 @@ async fn convert_bytes_to_folder(bytes: Bytes, zip_path: &str) {
209247

210248
match fs::remove_file(zip_path) {
211249
Ok(_) => info("Temporary zip file removed".to_string()),
212-
Err(e) => error_without_trace(format!("Warning: Failed to remove temporary zip file: {e}")),
250+
Err(e) => error_without_trace(format!(
251+
"Warning: Failed to remove temporary zip file: {:?}",
252+
e
253+
)),
213254
}
214255
}
215256

216257
async fn filter_features(selected_features: Vec<String>) {
217258
let definitions_path = "./definitions";
218259

219-
match fs::read_dir(definitions_path) {
220-
Ok(entries) => {
221-
for entry in entries {
222-
let directory = match entry {
223-
Ok(directory) => directory,
224-
Err(e) => {
225-
panic!(
226-
"{}",
227-
format!("Warning: Failed to read directory entry: {e}")
228-
);
229-
}
230-
};
260+
let entries = match fs::read_dir(definitions_path) {
261+
Ok(entries) => entries,
262+
Err(e) => {
263+
error_without_trace(format!("Failed to read definitions directory: {:?}", e));
264+
return;
265+
}
266+
};
231267

232-
let name = directory.file_name().to_str().unwrap_or("").to_string();
268+
for entry in entries {
269+
let directory = match entry {
270+
Ok(directory) => directory,
271+
Err(e) => {
272+
panic!("Warning: Failed to read directory entry {:?}", e);
273+
}
274+
};
233275

234-
if !selected_features.contains(&name) {
235-
match fs::remove_dir_all(directory.path()) {
236-
Ok(_) => {}
237-
Err(e) => {
238-
error_without_trace(format!("Warning: Failed to remove directory: {e}"))
239-
}
240-
}
276+
let name = directory.file_name().to_str().unwrap_or("").to_string();
277+
278+
if !selected_features.contains(&name) {
279+
match fs::remove_dir_all(directory.path()) {
280+
Ok(_) => {}
281+
Err(e) => {
282+
error_without_trace(format!("Warning: Failed to remove directory: {:?}", e))
241283
}
242284
}
243285
}
244-
Err(e) => {
245-
error_without_trace(format!("Failed to read definitions directory: {e}"));
246-
}
247286
}
248287
}

0 commit comments

Comments
 (0)