bee_tui/manifest_walker.rs
1//! Async walker for Mantaray manifests.
2//!
3//! Bee-rs 1.6 exposes [`bee::manifest::MantarayNode`] +
4//! [`bee::manifest::unmarshal`] but no recursive-load API; this
5//! module implements lazy fork-loading on top of the chunk-download
6//! primitive. Each call fetches a single chunk and parses it into a
7//! `MantarayNode` (or surfaces a recoverable error when the data is
8//! not a valid manifest, mirroring the swarm-cli pattern).
9//!
10//! The S12 Manifests screen owns the tree-walk state machine (which
11//! forks the operator expanded, per-fork load status, cursor position).
12//! This module is the I/O leaf — pure-fn-ish: takes a reference, hits
13//! the network, returns a parsed node or an error.
14
15use std::sync::Arc;
16
17use bee::manifest::{MantarayNode, unmarshal};
18use bee::swarm::Reference;
19
20use crate::api::ApiClient;
21
22/// Outcome of inspecting a reference. Distinguishes "this parses as a
23/// manifest" from "this is a raw chunk we couldn't parse" so the
24/// caller can route a `:inspect` invocation correctly.
25#[derive(Debug, Clone)]
26pub enum InspectResult {
27 /// The chunk parsed as a Mantaray manifest. `node` is the parsed
28 /// root; `bytes_len` is the raw chunk size for display.
29 Manifest { node: Box<MantarayNode>, bytes_len: usize },
30 /// The chunk fetched but didn't parse as a manifest. We surface
31 /// the size so the caller can display "raw, X bytes".
32 RawChunk { bytes_len: usize },
33 /// The fetch itself failed (network or 404). String is the error
34 /// for display.
35 Error(String),
36}
37
38/// Fetch a Mantaray chunk by reference and parse it. Returns the
39/// parsed node on success; on parse-failure surfaces the underlying
40/// error string ("mantaray: invalid version hash" etc).
41pub async fn load_node(
42 api: Arc<ApiClient>,
43 reference: Reference,
44) -> Result<MantarayNode, String> {
45 let bytes = api
46 .bee()
47 .file()
48 .download_chunk(&reference, None)
49 .await
50 .map_err(|e| format!("download_chunk: {e}"))?;
51 unmarshal(&bytes, reference.as_bytes()).map_err(|e| format!("unmarshal: {e}"))
52}
53
54/// Universal "what is this thing?" probe. Fetches the chunk; tries
55/// `MantarayNode::unmarshal`; on parse-success returns
56/// `InspectResult::Manifest`, on parse-failure returns
57/// `InspectResult::RawChunk` (the operator's chunk is real but isn't
58/// a manifest — just a raw blob).
59pub async fn inspect(api: Arc<ApiClient>, reference: Reference) -> InspectResult {
60 let bytes = match api.bee().file().download_chunk(&reference, None).await {
61 Ok(b) => b,
62 Err(e) => return InspectResult::Error(format!("download_chunk: {e}")),
63 };
64 let len = bytes.len();
65 match unmarshal(&bytes, reference.as_bytes()) {
66 Ok(node) => InspectResult::Manifest {
67 node: Box::new(node),
68 bytes_len: len,
69 },
70 Err(_) => InspectResult::RawChunk { bytes_len: len },
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn inspect_result_variants_compile_and_clone() {
80 // Smoke test: the enum's variant shape is what callers expect
81 // and `Clone` is correctly derived (the screen state copies
82 // these around). Doesn't exercise the network path; that
83 // lives in integration tests.
84 let raw = InspectResult::RawChunk { bytes_len: 4096 };
85 let raw2 = raw.clone();
86 match raw2 {
87 InspectResult::RawChunk { bytes_len } => assert_eq!(bytes_len, 4096),
88 _ => panic!("variant mismatch"),
89 }
90 let err = InspectResult::Error("net fail".into());
91 let err2 = err.clone();
92 match err2 {
93 InspectResult::Error(s) => assert_eq!(s, "net fail"),
94 _ => panic!("variant mismatch"),
95 }
96 }
97}