use crate::{WiFiError, WiFiResult, utils::ParseNum};
#[derive(Default, Debug)]
pub struct Info {
pub wiphy: usize,
pub ifindex: usize,
pub mode: Option<String>,
}
impl Info {
pub fn parse(s: &str) -> WiFiResult<Self> {
let mut lines = s.trim().lines();
let interface = lines.next().ok_or(WiFiError::new_system("no Interface"))?;
if !interface.contains("Interface") {
Err(WiFiError::new_system("no Interface"))?;
}
let mut out = Info::default();
for line in lines {
let parts = line.split_whitespace().collect::<Vec<_>>();
match parts[0] {
"wiphy" => {
out.wiphy = parts[1].parse_num()?;
}
"ifindex" => {
out.ifindex = parts[1].parse_num()?;
}
"type" => {
if parts.len() > 1 {
out.mode = Some(parts[1].to_string());
}
}
_ => {}
}
}
Ok(out)
}
}
#[derive(Default, Debug, Clone)]
pub struct PhyInfo {
pub bands: Vec<BandInfo>,
}
impl PhyInfo {
pub fn parse(raw: &str) -> WiFiResult<Self> {
let (_title, raws) = tab_children(raw)?;
let mut bands = vec![];
for raw in &raws {
if raw.starts_with("Band ") {
bands.push(BandInfo::parse(raw)?);
}
}
Ok(Self { bands })
}
}
#[derive(Default, Debug, Clone)]
pub struct BandInfo {
pub capabilities: BandCapabilities,
pub vht_capabilities: BandVHTCapabilities,
pub frequencies: Vec<f32>,
}
impl BandInfo {
pub fn parse(raw: &str) -> WiFiResult<Self> {
let (_title, raws) = tab_children(raw)?;
let mut out = Self::default();
for raw in &raws {
if raw.starts_with("Capabilities") {
out.capabilities = BandCapabilities::parse(raw)?;
}
if raw.starts_with("VHT Capabilities") {
out.vht_capabilities = BandVHTCapabilities::parse(raw)?;
}
if raw.starts_with("Frequencies") {
let (_title, raws) = tab_children(raw)?;
for raw in &raws {
let elems = raw.trim().split_ascii_whitespace().collect::<Vec<_>>();
out.frequencies.push(
elems
.get(1)
.ok_or(WiFiError::new_system("fmt err"))?
.parse()?,
);
}
}
}
Ok(out)
}
}
#[derive(Default, Debug, Clone)]
pub struct BandCapabilities {
pub rx_ht20_sgi: bool,
pub rx_ht40_sgi: bool,
}
impl BandCapabilities {
pub fn parse(raw: &str) -> WiFiResult<Self> {
let (_title, raws) = tab_children(raw)?;
let mut out = Self::default();
for raw in &raws {
if raw.contains("RX HT20 SGI") {
out.rx_ht20_sgi = true;
}
if raw.contains("RX HT40 SGI") {
out.rx_ht40_sgi = true;
}
}
Ok(out)
}
}
#[derive(Default, Debug, Clone)]
pub struct BandVHTCapabilities {
pub mhz80: bool,
pub mhz160: bool,
}
impl BandVHTCapabilities {
pub fn parse(raw: &str) -> WiFiResult<Self> {
let (_title, raws) = tab_children(raw)?;
let mut out = Self::default();
for raw in &raws {
if raw.contains("80 MHz") {
out.mhz80 = true;
}
if raw.contains("160/80+80 MHz") {
out.mhz160 = true;
}
}
Ok(out)
}
}
fn tab_children(raw: &str) -> WiFiResult<(String, Vec<String>)> {
let mut lines = raw.trim().lines();
let mut children = vec![];
let title = lines
.next()
.ok_or(WiFiError::new_system("no title"))?
.to_string();
let mut content = String::new();
for l in lines {
if l.as_bytes().first() != Some(&b'\t') {
Err(WiFiError::new_system("child not start with tab"))?;
}
let l = String::from_utf8_lossy(&l.as_bytes()[1..]).to_string();
if l.as_bytes().first() != Some(&b'\t') && !content.is_empty() {
children.push(content.trim().to_string());
content = String::new();
}
content += format!("{l}\r\n").as_str();
}
if !content.is_empty() {
children.push(content);
}
Ok((title, children))
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_info() {
let s = include_str!("data/info.txt");
println!("{s}");
let info = Info::parse(s).unwrap();
assert_eq!(info.wiphy, 1);
assert_eq!(info.ifindex, 5);
}
#[test]
fn test_phy_info() {
let s = include_str!("data/phy_info.txt");
let i = PhyInfo::parse(s).unwrap();
println!("{i:?}")
}
}