Download a change file directly to disk. Downloads the change and writes it directly to a file on disk, avoiding the need to hold the full change in memory as a `Bytes` before persisting. # Arguments `hash` - The base32-encoded hash of the change. `dest` - The destination file path. # Returns The number of bytes written to disk.
(&self, hash: &str, dest: &Path)
| 30 | /// |
| 31 | /// The number of bytes written to disk. |
| 32 | pub async fn download_change_to_file(&self, hash: &str, dest: &Path) -> RemoteResult<u64> { |
| 33 | let url = format!("{}?change={}", self.base_url, hash); |
| 34 | debug!("GET change (to file): {} → {:?}", url, dest); |
| 35 | |
| 36 | let response = self |
| 37 | .client |
| 38 | .get(&url) |
| 39 | .send() |
| 40 | .await |
| 41 | .map_err(|e| RemoteError::connection_failed(&url, e))?; |
| 42 | |
| 43 | crate::check_min_version_header(response.headers()); |
| 44 | let status = response.status(); |
| 45 | |
| 46 | match status { |
| 47 | StatusCode::OK => { |
| 48 | let bytes = response |
| 49 | .bytes() |
| 50 | .await |
| 51 | .map_err(|e| RemoteError::connection_failed(&url, e))?; |
| 52 | |
| 53 | // Create parent directory if needed |
| 54 | if let Some(parent) = dest.parent() { |
| 55 | tokio::fs::create_dir_all(parent).await.map_err(|e| { |
| 56 | RemoteError::other(format!( |
| 57 | "Failed to create directory {:?}: {}", |
| 58 | parent, e |
| 59 | )) |
| 60 | })?; |
| 61 | } |
| 62 | |
| 63 | tokio::fs::write(dest, &bytes).await.map_err(|e| { |
| 64 | RemoteError::other(format!("Failed to write file {:?}: {}", dest, e)) |
| 65 | })?; |
| 66 | |
| 67 | let bytes_written = bytes.len() as u64; |
| 68 | debug!( |
| 69 | "Downloaded change {} to {:?} ({} bytes)", |
| 70 | hash, dest, bytes_written |
| 71 | ); |
| 72 | Ok(bytes_written) |
| 73 | } |
| 74 | StatusCode::NOT_FOUND => Err(RemoteError::change_not_found(hash)), |
| 75 | StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => { |
| 76 | let msg = response.text().await.unwrap_or_default(); |
| 77 | Err(RemoteError::auth_failed(&url, msg)) |
| 78 | } |
| 79 | _ => { |
| 80 | let msg = response.text().await.unwrap_or_default(); |
| 81 | Err(RemoteError::http(status.as_u16(), msg)) |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | /// Download a change with layer-selective filtering. |
| 87 | /// |