Fetch a view's manifest from the remote. The manifest is the view's complete identity — header line `name\tscope\tparent\tstate` followed by the view's change log, one base32 hash per line, exactly as stored on the remote. Returns `Ok(None)` if the remote does not have the view, and [`RemoteError::protocol`] if the server predates manifest support (callers decide whether that is fatal — e.g. draf
(&self, view: &str)
| 439 | /// This method is string-level: parsing into a typed manifest happens in |
| 440 | /// `atomic-repository`, which owns the format. |
| 441 | pub async fn get_view_manifest(&self, view: &str) -> RemoteResult<Option<String>> { |
| 442 | let url = format!("{}?view-manifest={}", self.base_url, view); |
| 443 | debug!("GET view-manifest: {}", url); |
| 444 | |
| 445 | let response = self |
| 446 | .client |
| 447 | .get(&url) |
| 448 | .send() |
| 449 | .await |
| 450 | .map_err(|e| RemoteError::connection_failed(&url, e))?; |
| 451 | |
| 452 | crate::check_min_version_header(response.headers()); |
| 453 | let status = response.status(); |
| 454 | |
| 455 | match status { |
| 456 | StatusCode::OK => { |
| 457 | let text = response |
| 458 | .text() |
| 459 | .await |
| 460 | .map_err(|e| RemoteError::connection_failed(&url, e))?; |
| 461 | // A manifest response is line-based with a tab-separated |
| 462 | // header. An older server answers `?view-manifest` with its |
| 463 | // generic JSON info blob — detect that and report missing |
| 464 | // support rather than handing garbage to the parser. |
| 465 | let looks_like_manifest = text |
| 466 | .lines() |
| 467 | .find(|l| !l.trim().is_empty()) |
| 468 | .map(|l| l.contains('\t')) |
| 469 | .unwrap_or(false); |
| 470 | if text.trim().is_empty() { |
| 471 | Ok(None) |
| 472 | } else if looks_like_manifest { |
| 473 | Ok(Some(text)) |
| 474 | } else { |
| 475 | Err(RemoteError::protocol( |
| 476 | "server does not support view manifests (?view-manifest)", |
| 477 | )) |
| 478 | } |
| 479 | } |
| 480 | StatusCode::NOT_FOUND => Ok(None), |
| 481 | StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => { |
| 482 | let msg = response.text().await.unwrap_or_default(); |
| 483 | Err(RemoteError::auth_failed(&url, msg)) |
| 484 | } |
| 485 | _ => { |
| 486 | let msg = response.text().await.unwrap_or_default(); |
| 487 | Err(RemoteError::http(status.as_u16(), msg)) |
| 488 | } |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | /// List all provenance graph hashes the remote holds. |
| 493 | /// |