wifi_manager/os/linux/
iw_res.rs

1use std::num::ParseIntError;
2
3use crate::{WiFiError, WiFiResult};
4
5#[derive(Default, Debug)]
6pub struct Info {
7    pub wiphy: usize,
8    pub ifindex: usize,
9}
10
11impl Info {
12    pub fn parse(s: &str) -> WiFiResult<Self> {
13        let mut lines = s.trim().lines();
14
15        let interface = lines.next().ok_or(WiFiError::new_system("no Interface"))?;
16
17        if !interface.contains("Interface") {
18            Err(WiFiError::new_system("no Interface"))?;
19        }
20
21        let mut out = Info::default();
22
23        for line in lines {
24            let parts = line.split_whitespace().collect::<Vec<_>>();
25            match parts[0] {
26                "wiphy" => {
27                    out.wiphy = parts[1].trim().parse::<usize>()?;
28                }
29                "ifindex" => {
30                    out.ifindex = parts[1].trim().parse::<usize>()?;
31                }
32                _ => {}
33            }
34        }
35
36        Ok(out)
37    }
38}
39
40impl From<ParseIntError> for WiFiError {
41    fn from(value: ParseIntError) -> Self {
42        WiFiError::new_system(format!("ParseIntError: {}", value))
43    }
44}
45
46#[derive(Default, Debug, Clone)]
47pub struct PhyInfo {
48    pub bands: Vec<BandInfo>,
49}
50
51impl PhyInfo {
52    pub fn parse(raw: &str) -> WiFiResult<Self> {
53        let (_title, raws) = tab_children(raw)?;
54        let mut bands = vec![];
55        for raw in &raws {
56            if raw.starts_with("Band ") {
57                bands.push(BandInfo::parse(raw)?);
58            }
59        }
60        Ok(Self { bands })
61    }
62}
63
64#[derive(Default, Debug, Clone)]
65pub struct BandInfo {
66    pub capabilities: BandCapabilities,
67    pub vht_capabilities: BandVHTCapabilities,
68    pub frequencies: Vec<i32>,
69}
70
71impl BandInfo {
72    pub fn parse(raw: &str) -> WiFiResult<Self> {
73        let (_title, raws) = tab_children(raw)?;
74        let mut out = Self::default();
75
76        for raw in &raws {
77            if raw.starts_with("Capabilities") {
78                out.capabilities = BandCapabilities::parse(raw)?;
79            }
80            if raw.starts_with("VHT Capabilities") {
81                out.vht_capabilities = BandVHTCapabilities::parse(raw)?;
82            }
83            if raw.starts_with("Frequencies") {
84                let (_title, raws) = tab_children(raw)?;
85                for raw in &raws {
86                    let elems = raw.trim().split_ascii_whitespace().collect::<Vec<_>>();
87
88                    out.frequencies.push(
89                        elems
90                            .get(1)
91                            .ok_or(WiFiError::new_system("fmt err"))?
92                            .parse()?,
93                    );
94                }
95            }
96        }
97
98        Ok(out)
99    }
100}
101
102#[derive(Default, Debug, Clone)]
103pub struct BandCapabilities {
104    pub rx_ht20_sgi: bool,
105    pub rx_ht40_sgi: bool,
106}
107
108impl BandCapabilities {
109    pub fn parse(raw: &str) -> WiFiResult<Self> {
110        let (_title, raws) = tab_children(raw)?;
111        let mut out = Self::default();
112
113        for raw in &raws {
114            if raw.contains("RX HT20 SGI") {
115                out.rx_ht20_sgi = true;
116            }
117            if raw.contains("RX HT40 SGI") {
118                out.rx_ht40_sgi = true;
119            }
120        }
121
122        Ok(out)
123    }
124}
125
126#[derive(Default, Debug, Clone)]
127pub struct BandVHTCapabilities {
128    pub mhz80: bool,
129    pub mhz160: bool,
130}
131
132impl BandVHTCapabilities {
133    pub fn parse(raw: &str) -> WiFiResult<Self> {
134        let (_title, raws) = tab_children(raw)?;
135        let mut out = Self::default();
136
137        for raw in &raws {
138            if raw.contains("80 MHz") {
139                out.mhz80 = true;
140            }
141            if raw.contains("160/80+80 MHz") {
142                out.mhz160 = true;
143            }
144        }
145
146        Ok(out)
147    }
148}
149
150fn tab_children(raw: &str) -> WiFiResult<(String, Vec<String>)> {
151    let mut lines = raw.trim().lines();
152    let mut children = vec![];
153
154    let title = lines
155        .next()
156        .ok_or(WiFiError::new_system("no title"))?
157        .to_string();
158    let mut content = String::new();
159    for l in lines {
160        if l.as_bytes().first() != Some(&b'\t') {
161            Err(WiFiError::new_system("child not start with tab"))?;
162        }
163
164        let l = String::from_utf8_lossy(&l.as_bytes()[1..]).to_string();
165
166        if l.as_bytes().first() != Some(&b'\t') && !content.is_empty() {
167            children.push(content.trim().to_string());
168            content = String::new();
169        }
170        content += format!("{l}\r\n").as_str();
171    }
172    if !content.is_empty() {
173        children.push(content);
174    }
175
176    Ok((title, children))
177}
178
179#[cfg(test)]
180mod test {
181    use super::*;
182
183    #[test]
184    fn test_info() {
185        let s = include_str!("data/info.txt");
186
187        println!("{s}");
188
189        let info = Info::parse(s).unwrap();
190
191        assert_eq!(info.wiphy, 1);
192        assert_eq!(info.ifindex, 5);
193    }
194
195    #[test]
196    fn test_phy_info() {
197        let s = include_str!("data/phy_info.txt");
198
199        let i = PhyInfo::parse(s).unwrap();
200
201        println!("{i:?}")
202    }
203}