dump_menuitems/dump_menuitems.rs
1//! Validation helper: decode and dump Apple Biome `App.MenuItem` records
2//! (application name + menu-item text + timestamp) from a SEGB stream.
3//!
4//! Where `dump_structure` reconciles the *container* (state / timestamp / CRC)
5//! against `ccl-segb`, this dumps the decoded *protobuf fields* so the field
6//! mapping itself — field 1 = `application`, field 2 = `menu_item` — can be
7//! reconciled against the *known* menu selections driven on a real macOS Tahoe
8//! system. That closes the gap neither `dump_structure` (container only) nor the
9//! iOS corpus (no menu bar) can: `ccl-segb` hands back raw payload bytes, so it
10//! cannot be the oracle for the protobuf interpretation — the driven selections
11//! are.
12//!
13//! Only `Written` records carry a live payload (a `Deleted` record's payload is
14//! wiped), matching the `segb-forensic` analyzer's Written-only audit.
15//!
16//! Usage: `cargo run -p segb-core --example dump_menuitems -- <segb-file>`
17#![allow(clippy::unwrap_used, clippy::expect_used)]
18
19use segb::common::EntryState;
20
21fn main() {
22 let path = std::env::args()
23 .nth(1)
24 .expect("usage: dump_menuitems <segb-file>");
25 let data = std::fs::read(&path).expect("read file");
26 let mut cur = std::io::Cursor::new(data);
27 let records = segb::read_segb(&mut cur).expect("parse SEGB");
28 let mut decoded = 0usize;
29 for (i, r) in records.iter().enumerate() {
30 if r.state() != EntryState::Written {
31 continue;
32 }
33 match segb::menuitem::decode_app_menu_item(r.payload(), r.timestamp_unix()) {
34 Ok(m) => {
35 println!(
36 " [{i}] application={:?} menu_item={:?} ts_unix={:?}",
37 m.application, m.menu_item, m.timestamp_unix
38 );
39 decoded += 1;
40 }
41 Err(e) => println!(" [{i}] decode_error={e}"),
42 }
43 }
44 println!("menu_items: {decoded}");
45}