Skip to main content

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 {
30        node: Box<MantarayNode>,
31        bytes_len: usize,
32    },
33    /// The chunk fetched but didn't parse as a manifest. We surface
34    /// the size so the caller can display "raw, X bytes".
35    RawChunk { bytes_len: usize },
36    /// The fetch itself failed (network or 404). String is the error
37    /// for display.
38    Error(String),
39}
40
41/// Fetch a Mantaray chunk by reference and parse it. Returns the
42/// parsed node on success; on parse-failure surfaces the underlying
43/// error string ("mantaray: invalid version hash" etc).
44pub async fn load_node(api: Arc<ApiClient>, reference: Reference) -> 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}