Get the chunk manifest for a change. The chunk manifest lists all content chunks in a change with their blake3 hashes and sizes. This is the starting point for delta transfer negotiation — the receiver compares the manifest against its local chunk inventory to determine which chunks need to be transferred. # Arguments `hash` - The base32-encoded hash of the change. # Returns The [`ChunkManife
(&self, hash: &str)
| 169 | /// The [`ChunkManifest`] for the change, or `None` if the server |
| 170 | /// doesn't support the `?manifest` endpoint (graceful degradation). |
| 171 | pub async fn get_chunk_manifest(&self, hash: &str) -> RemoteResult<Option<ChunkManifest>> { |
| 172 | let url = format!("{}?change={}&manifest", self.base_url, hash); |
| 173 | debug!("GET chunk manifest: {}", url); |
| 174 | |
| 175 | let response = self |
| 176 | .client |
| 177 | .get(&url) |
| 178 | .send() |
| 179 | .await |
| 180 | .map_err(|e| RemoteError::connection_failed(&url, e))?; |
| 181 | |
| 182 | crate::check_min_version_header(response.headers()); |
| 183 | let status = response.status(); |
| 184 | |
| 185 | match status { |
| 186 | StatusCode::OK => { |
| 187 | let content_type = response |
| 188 | .headers() |
| 189 | .get(CONTENT_TYPE) |
| 190 | .and_then(|v| v.to_str().ok()) |
| 191 | .unwrap_or(""); |
| 192 | |
| 193 | // If the server returns JSON, it supports the manifest endpoint |
| 194 | if content_type.contains("json") { |
| 195 | let text = response |
| 196 | .text() |
| 197 | .await |
| 198 | .map_err(|e| RemoteError::connection_failed(&url, e))?; |
| 199 | |
| 200 | let manifest: ChunkManifest = serde_json::from_str(&text).map_err(|e| { |
| 201 | RemoteError::protocol(format!("Failed to parse chunk manifest: {}", e)) |
| 202 | })?; |
| 203 | |
| 204 | debug!( |
| 205 | "Got chunk manifest for {}: {} chunks, {} compressed", |
| 206 | hash, |
| 207 | manifest.chunk_count(), |
| 208 | manifest.total_compressed(), |
| 209 | ); |
| 210 | Ok(Some(manifest)) |
| 211 | } else { |
| 212 | // Server returned the full change data instead of a manifest. |
| 213 | // This means it doesn't support ?manifest — graceful degradation. |
| 214 | debug!( |
| 215 | "Server doesn't support ?manifest (returned {}, not JSON)", |
| 216 | content_type |
| 217 | ); |
| 218 | Ok(None) |
| 219 | } |
| 220 | } |
| 221 | StatusCode::NOT_FOUND => Err(RemoteError::change_not_found(hash)), |
| 222 | StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => { |
| 223 | let msg = response.text().await.unwrap_or_default(); |
| 224 | Err(RemoteError::auth_failed(&url, msg)) |
| 225 | } |
| 226 | // 400 or other errors may indicate the server doesn't support ?manifest |
| 227 | StatusCode::BAD_REQUEST => { |
| 228 | debug!("Server returned 400 for ?manifest — likely unsupported"); |
nothing calls this directly
no test coverage detected