Files
RedBear-OS/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase5-wifi-analyze.rs
T
2026-04-16 12:45:07 +01:00

154 lines
5.0 KiB
Rust

use std::fs;
use std::process;
use redbear_hwutils::parse_args;
use serde_json::Value;
const PROGRAM: &str = "redbear-phase5-wifi-analyze";
const USAGE: &str =
"Usage: redbear-phase5-wifi-analyze <capture.json>\n\nSummarize a Wi-Fi capture bundle into likely blocker categories.";
fn read_text<'a>(value: &'a Value, path: &[&str]) -> &'a str {
let mut current = value;
for segment in path {
match current.get(*segment) {
Some(next) => current = next,
None => return "",
}
}
current.as_str().unwrap_or("")
}
fn classify(capture: &Value) -> Vec<&'static str> {
let mut out = Vec::new();
let driver_probe = read_text(capture, &["commands", "driver_probe", "stdout"]);
let connect = read_text(capture, &["commands", "phase5_wifi_check", "stdout"]);
let connect_result = read_text(capture, &["scheme", "connect_result", "value"]);
let disconnect_result = read_text(capture, &["scheme", "disconnect_result", "value"]);
let last_error = read_text(capture, &["scheme", "last_error", "value"]);
let netctl_status = read_text(capture, &["commands", "netctl_status", "stdout"]);
let redbear_info = read_text(capture, &["commands", "redbear_info_json", "stdout"]);
if !driver_probe.contains("candidates=") || driver_probe.contains("candidates=0") {
out.push("device-detection");
}
if connect.contains("missing firmware") || last_error.contains("firmware") {
out.push("firmware");
}
if connect_result.is_empty() || connect_result.contains("not-run") {
out.push("association-control-path");
}
if disconnect_result.is_empty() || disconnect_result.contains("not-run") {
out.push("disconnect-lifecycle");
}
if !netctl_status.contains("address=") || netctl_status.contains("address=unknown") {
out.push("dhcp-or-addressing");
}
if !redbear_info.contains("wifi_connect_result")
|| !redbear_info.contains("wifi_disconnect_result")
{
out.push("reporting-surface");
}
if last_error.contains("timed out") || last_error.contains("failed") {
out.push("runtime-failure");
}
if out.is_empty() {
out.push("bounded-lifecycle-pass-no-real-link-proof");
}
out
}
fn run() -> Result<(), String> {
let args: Vec<String> = std::env::args().collect();
parse_args(PROGRAM, USAGE, args.clone().into_iter()).map_err(|err| {
if err.is_empty() {
process::exit(0);
}
err
})?;
let path = args
.get(1)
.ok_or_else(|| "missing capture.json path".to_string())?;
let text = fs::read_to_string(path).map_err(|err| format!("failed to read {}: {err}", path))?;
let capture: Value = serde_json::from_str(&text)
.map_err(|err| format!("failed to parse {} as JSON: {err}", path))?;
println!("=== Red Bear Wi-Fi Capture Analysis ===");
println!("capture={path}");
println!(
"profile={}",
capture
.get("profile")
.and_then(Value::as_str)
.unwrap_or("unknown")
);
println!(
"interface={}",
capture
.get("interface")
.and_then(Value::as_str)
.unwrap_or("unknown")
);
let classes = classify(&capture);
println!("classification={}", classes.join(","));
for item in classes {
println!("blocker={item}");
}
Ok(())
}
fn main() {
if let Err(err) = run() {
eprintln!("{PROGRAM}: {err}");
process::exit(1);
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn classify_flags_missing_detection() {
let capture = json!({
"commands": {
"driver_probe": {"stdout": "candidates=0"},
"phase5_wifi_check": {"stdout": ""},
"netctl_status": {"stdout": "address=unknown"},
"redbear_info_json": {"stdout": "{}"}
},
"scheme": {
"connect_result": {"value": ""},
"disconnect_result": {"value": ""},
"last_error": {"value": ""}
}
});
let classes = classify(&capture);
assert!(classes.contains(&"device-detection"));
}
#[test]
fn classify_pass_path_when_only_bounded_state_exists() {
let capture = json!({
"commands": {
"driver_probe": {"stdout": "candidates=1"},
"phase5_wifi_check": {"stdout": "PASS: bounded Intel Wi-Fi runtime path exercised on target"},
"netctl_status": {"stdout": "address=10.0.0.44/24"},
"redbear_info_json": {"stdout": "wifi_connect_result wifi_disconnect_result"}
},
"scheme": {
"connect_result": {"value": "connect_result=bounded-associated"},
"disconnect_result": {"value": "disconnect_result=bounded-disconnected"},
"last_error": {"value": "none"}
}
});
let classes = classify(&capture);
assert_eq!(classes, vec!["bounded-lifecycle-pass-no-real-link-proof"]);
}
}