wifi_manager/os/linux/
iw_res.rs1use 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}