use crate::connectors::types::{ Connects, ListsNetworkInterfaces, ListsNetworks, Network, NetworkInterface, }; use crate::connectors::utils::call_exec; use std::borrow::BorrowMut; use std::str::Split; pub struct NetworkManagerConnector {} impl NetworkManagerConnector { fn call_nmcli(&self, add_args: Vec<&str>) -> Result, String> { let args = vec!["-t", "-m", "tabular", "-c", "no", "-o", "-e", "yes"] .into_iter() .chain(add_args.into_iter()) .collect(); call_exec("networkmanager", "nmcli", args) } fn get_nmcli(&self, add_args: Vec<&str>) -> Result>, String> { let output = self .call_nmcli(add_args) .expect("calling networkmanager failed"); let res = String::from_utf8(output).expect("networkmanager returned non-utf8"); let lines: Vec<&str> = res.split("\n").filter(|ln| !ln.is_empty()).collect(); let lines_with_fields: Vec> = lines.into_iter().map(|line| line.split(":")).collect(); let mut parsed: Vec> = vec![]; for old_fields_immut in lines_with_fields.clone() { let mut old_fields = old_fields_immut; let mut new_fields: Vec = vec![]; let mut continuation = false; loop { match old_fields.next() { Some(field) => { let mut new_field = match continuation { true => new_fields.pop().expect("new field").to_string(), false => "".to_string(), }; if continuation { new_field.push(':'); } new_field.push_str(match field.strip_suffix("\\") { Some(stripped) => stripped, None => field, }); new_fields.push(new_field); continuation = field.ends_with("\\"); }, None => { let parsed_line = new_fields; parsed.push(parsed_line); break }, } } } Ok(parsed) } } impl ListsNetworkInterfaces for NetworkManagerConnector { fn list_network_interfaces(&self) -> Result, String> { let iface_list = match self.get_nmcli( vec!["-f", "DEVICE,TYPE,STATE,CONNECTION", "device"], ) { Ok(list) => list, Err(err) => return Err(err), }; let ifaces = iface_list.into_iter().map(|ifa| NetworkInterface { machine_name: ifa[0].clone(), enabled: ifa[2] != "unavailable", ..Default::default() }).collect(); Ok(ifaces) } } impl ListsNetworks for NetworkManagerConnector { fn list_networks(&self, iface: &NetworkInterface) -> Result, String> { let net_list = match self.get_nmcli(vec![ "-f", "SSID,BSSID,CHAN,SIGNAL,WPA-FLAGS,IN-USE", "device", "wifi", "list", "ifname", &iface.machine_name], ) { Ok(list) => list, Err(err) => return Err(err), }; let nets = net_list.into_iter().map(|net| Network { ssid: net[0].to_string(), bssid: Some(net[1].to_string()), channel: Some(net[2].parse::().unwrap()), rssi: Some(net[3].parse::().unwrap()), ..Default::default() }).collect(); Ok(nets) } }