Refactor(Project): Change Project Structure

This commit is contained in:
Tochukwu Nkemdilim
2018-07-19 16:14:34 +01:00
parent e8b83bf245
commit 65c641670d
17 changed files with 268 additions and 250 deletions
+26 -11
View File
@@ -50,20 +50,35 @@ fn main() -> Result<(), WifiConnectionError> {
} }
``` ```
## To do ## Features
- [x] Bundle windows profile sample as literals ### Windows
- [x] Add support for Windows - [x] Support for Windows.
- [x] Add support for linux - [x] Bundle windows profile sample as literals.
- [x] Add disconnect feature - [x] Add hotspot functionality.
- [x] Use `tempfile` crate on windows to generate windows profile temporary file. - [x] Use `tempfile` crate on windows to generate windows profile temporary file.
- [ ] Add support for OSX - [ ] Add get network type feature.
- [ ] Fix the implementation for `check_if_wifi_is_enabled` for windows. - [ ] Fix the implementation for `check_if_wifi_is_enabled` for windows.
- [ ] Add get network type feature - [ ] Add implementation for WifiInterface trait.
- [ ] Add create hotspot functionality
- [ ] Write documentation
- [ ] Write tests ### Linux
- [ ] Add multi-threaded support - [x] Support for linux.
- [x] Add disconnect feature.
- [ ] Add hotspot functionality.
- [ ] Add get network type feature.
### OsX
- [x] Add support for OSX.
- [ ] Add get network type feature.
- [ ] Add hotspot functionality.
### General
- [x] Return detailed error messages.
- [ ] Write documentation. **(approx. percentage: 20%)**
- [ ] Update `wifi-CLI` with recent updates.
- [ ] Write tests.
- [ ] Add multi-threaded support.
# Contribution # Contribution
+7 -12
View File
@@ -1,11 +1,9 @@
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod handlers; mod handlers;
mod providers;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod stubs; mod stubs;
mod providers;
use platforms::WifiError; use platforms::WifiError;
use std::{fmt, io}; use std::{fmt, io};
@@ -17,20 +15,17 @@ pub trait Network: fmt::Debug {
fn disconnect(&self) -> Result<bool, WifiConnectionError>; fn disconnect(&self) -> Result<bool, WifiConnectionError>;
} }
/// Configuration for a wifi network.
#[derive(Debug, Clone)]
pub struct Config<'a> {
pub interface: Option<&'a str>,
}
#[derive(Debug)] #[derive(Debug)]
pub enum WifiConnectionError { pub enum WifiConnectionError {
SsidNotFound, // SsidNotFound,
IpAssignFailed, // IpAssignFailed,
#[cfg(target_os = "windows")]
AddNetworkProfileFailed, AddNetworkProfileFailed,
FailedToConnect(String), FailedToConnect(String),
FailedToDisconnect(String), FailedToDisconnect(String),
Other { kind: WifiError }, Other {
kind: WifiError,
},
} }
impl From<io::Error> for WifiConnectionError { impl From<io::Error> for WifiConnectionError {
+8 -2
View File
@@ -1,9 +1,15 @@
use connectivity::{Network, WifiConnectionError}; use connectivity::{Network, WifiConnectionError};
use platforms::{Connection, Linux}; use platforms::{Connection, WiFi, WifiError, WifiInterface};
use std::process::Command; use std::process::Command;
impl Network for Linux { impl Network for WiFi {
fn connect(&mut self, ssid: &str, password: &str) -> Result<bool, WifiConnectionError> { fn connect(&mut self, ssid: &str, password: &str) -> Result<bool, WifiConnectionError> {
if !WiFi::is_wifi_enabled().map_err(|err| WifiConnectionError::Other { kind: err })? {
return Err(WifiConnectionError::Other {
kind: WifiError::InterfaceDisabled,
});
}
let output = Command::new("nmcli") let output = Command::new("nmcli")
.args(&[ .args(&[
"d", "d",
+1 -1
View File
@@ -4,5 +4,5 @@ mod linux;
#[cfg(target_os = "osx")] #[cfg(target_os = "osx")]
mod osx; mod osx;
// #[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod windows; mod windows;
+21 -60
View File
@@ -1,40 +1,40 @@
use connectivity::{Network, WifiConnectionError}; use connectivity::{Network, WifiConnectionError};
use platforms::{Connection, WiFi, WifiError, WifiInterface};
use std::process::Command; use std::process::Command;
#[derive(Debug)] impl Network for WiFi {
pub struct OSX { fn connect(&mut self, ssid: &str, password: &str) -> Result<bool, WifiConnectionError> {
pub name: String, if !WiFi::is_wifi_enabled().map_err(|err| WifiConnectionError::Other { kind: err })? {
interface: String, return Err(WifiConnectionError::Other {
} kind: WifiError::InterfaceDisabled,
});
impl OSX {
#[cfg(target_os = "windows")]
pub fn new(name: &str, interface: Option<&str>) -> Self {
OSX {
name: name.into(),
interface: interface.unwrap_or("en0").into(),
} }
}
}
impl Network for OSX {
fn connect(&self, password: &str) -> Result<bool, WifiConnectionError> {
let output = Command::new("networksetup") let output = Command::new("networksetup")
.args(&["-setairportnetwork", &self.interface, &self.name, &password]) .args(&["-setairportnetwork", &self.interface, &ssid, &password])
.output() .output()
.map_err(|err| WifiConnectionError::FailedToConnect(format!("{}", err)))?; .map_err(|err| WifiConnectionError::FailedToConnect(format!("{}", err)))?;
Ok(String::from_utf8_lossy(&output.stdout) if !String::from_utf8_lossy(&output.stdout)
.as_ref() .as_ref()
.contains("successfully activated")) .contains("successfully activated")
{
return Ok(false);
}
self.connection = Some(Connection {
ssid: String::from(ssid),
});
Ok(true)
} }
fn disconnect(&self) -> Result<bool, WifiConnectionError> { fn disconnect(&self) -> Result<bool, WifiConnectionError> {
let output = Command::new("networksetup") let output = Command::new("networksetup")
.args(&[ .args(&[
"-removepreferredwirelessnetwork", "-removepreferredwirelessnetwork",
&self.interface, &*self.interface,
&self.name, &*self.connection.as_ref().unwrap().ssid,
]) ])
.output() .output()
.map_err(|err| WifiConnectionError::FailedToDisconnect(format!("{}", err)))?; .map_err(|err| WifiConnectionError::FailedToDisconnect(format!("{}", err)))?;
@@ -43,43 +43,4 @@ impl Network for OSX {
.as_ref() .as_ref()
.contains("disconnect")) .contains("disconnect"))
} }
fn is_wifi_enabled(&self) -> bool {
let output = Command::new("networksetup")
.args(&["radio", "wifi"])
.output();
if let Err(_) = output {
return false;
}
String::from_utf8_lossy(&output.unwrap().stdout)
.replace(" ", "")
.replace("\n", "")
.contains("enabled")
}
fn connnection_up(&self) -> bool {
let output = Command::new("networksetup")
.args(&["-setairportpower", &self.interface, "on"])
.output();
if let Err(_) = output {
return false;
}
false
}
fn connnection_down(&self) -> bool {
let output = Command::new("networksetup")
.args(&["-setairportpower", &self.interface, "off"])
.output();
if let Err(_) = output {
return false;
}
false
}
} }
+28 -16
View File
@@ -1,21 +1,14 @@
use connectivity::handlers::NetworkXmlProfileHandler; use connectivity::handlers::NetworkXmlProfileHandler;
use connectivity::{Network, WifiConnectionError}; use connectivity::{Network, WifiConnectionError};
use platforms::Windows; use platforms::{Connection, WiFi, WifiError, WifiInterface};
use std::process::Command; use std::process::Command;
impl Windows { impl WiFi {
#[cfg(target_os = "windows")] fn add_profile(ssid: &str, password: &str) -> Result<(), WifiConnectionError> {
pub fn new(name: &str, _interface: Option<&str>) -> Self {
Windows {
name: String::from(name),
}
}
pub(crate) fn add_profile(&self, password: &str) -> Result<(), WifiConnectionError> {
let mut handler = NetworkXmlProfileHandler::new(); let mut handler = NetworkXmlProfileHandler::new();
handler.content = handler handler.content = handler
.content .content
.replace("{SSID}", &self.name) .replace("{SSID}", ssid)
.replace("{password}", password); .replace("{password}", password);
let temp_file = handler.write_to_temp_file()?; let temp_file = handler.write_to_temp_file()?;
@@ -36,17 +29,36 @@ impl Windows {
} }
impl Network for Windows { impl Network for Windows {
fn connect(&self, password: &str) -> Result<bool, WifiConnectionError> { fn connect(&mut self, ssid: &str, password: &str) -> Result<bool, WifiConnectionError> {
self.add_profile(password)?; if !WiFi::is_wifi_enabled().map_err(|err| WifiConnectionError::Other { kind: err })? {
return Err(WifiConnectionError::Other {
kind: WifiError::InterfaceDisabled,
});
}
Self::add_profile(ssid, password)?;
let output = Command::new("netsh") let output = Command::new("netsh")
.args(&["wlan", "connect", &format!("name={}", self.name)]) .args(&[
"wlan",
"connect",
&format!("name={}", *self.connection.unwrap().ssid),
])
.output() .output()
.map_err(|err| WifiConnectionError::FailedToConnect(format!("{}", err)))?; .map_err(|err| WifiConnectionError::FailedToConnect(format!("{}", err)))?;
Ok(String::from_utf8_lossy(&output.stdout) if !String::from_utf8_lossy(&output.stdout)
.as_ref() .as_ref()
.contains("successfully activated")) .contains("successfully activated")
{
return Ok(false);
}
self.connection = Some(Connection {
ssid: String::from(ssid),
});
Ok(true)
} }
fn disconnect(&self) -> Result<bool, WifiConnectionError> { fn disconnect(&self) -> Result<bool, WifiConnectionError> {
+9 -4
View File
@@ -1,6 +1,5 @@
mod providers; mod providers;
// pub use self::providers::Machine;
use platforms::{WifiError, WifiInterface}; use platforms::{WifiError, WifiInterface};
use std::fmt; use std::fmt;
@@ -15,13 +14,19 @@ pub trait WifiHotspot: fmt::Debug + WifiInterface {
/// Creates wifi hotspot service for host machine. This only creats the wifi network, /// Creates wifi hotspot service for host machine. This only creats the wifi network,
/// and isn't responsible for initiating the serving of the wifi network process. /// and isn't responsible for initiating the serving of the wifi network process.
/// To begin serving the hotspot, use ```start_hotspot()```. /// To begin serving the hotspot, use ```start_hotspot()```.
fn create_hotspot(ssid: &str, password: &str) -> Result<bool, WifiHotspotError>; fn create_hotspot(ssid: &str, password: &str) -> Result<bool, WifiHotspotError> {
unimplemented!();
}
/// Start serving publicly an already created wifi hotspot. /// Start serving publicly an already created wifi hotspot.
fn start_hotspot() -> Result<bool, WifiHotspotError>; fn start_hotspot() -> Result<bool, WifiHotspotError> {
unimplemented!();
}
/// Stop serving a wifi network. /// Stop serving a wifi network.
/// ///
/// > All users connected will automatically be disconnected. /// > All users connected will automatically be disconnected.
fn stop_hotspot() -> Result<bool, WifiHotspotError>; fn stop_hotspot() -> Result<bool, WifiHotspotError> {
unimplemented!();
}
} }
+3 -30
View File
@@ -1,31 +1,4 @@
use hotspot::{WifiHotspot, WifiHotspotError}; use hotspot::WifiHotspot;
use platforms::Linux; use platforms::WiFi;
// #[derive(Debug)] impl WifiHotspot for WiFi {}
// pub struct Linux {
// pub name: String,
// interface: String,
// }
// impl Linux {
// pub fn new(name: &str, interface: Option<&str>) -> Self {
// Linux {
// name: name.into(),
// interface: interface.unwrap_or("wlan0").into(),
// }
// }
// }
impl WifiHotspot for Linux {
fn create_hotspot(ssid: &str, password: &str) -> Result<bool, WifiHotspotError> {
unimplemented!();
}
fn start_hotspot() -> Result<bool, WifiHotspotError> {
unimplemented!();
}
fn stop_hotspot() -> Result<bool, WifiHotspotError> {
unimplemented!();
}
}
+6 -12
View File
@@ -1,14 +1,8 @@
// #[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
// mod windows; mod windows;
// #[cfg(target_os = "windows")]
// pub use self::windows::Windows as Machine;
// #[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
// mod linux; mod linux;
// #[cfg(target_os = "linux")]
// pub use self::linux as Machine;
// #[cfg(target_os = "osx")] #[cfg(target_os = "osx")]
// mod osx; mod osx;
// #[cfg(target_os = "osx")]
// pub use self::osx::OSX as Machine;
+2 -15
View File
@@ -1,18 +1,5 @@
use connectivity::{Network, WifiConnectionError}; use connectivity::{Network, WifiConnectionError};
use platforms::WiFi;
use std::process::Command; use std::process::Command;
#[derive(Debug)] impl WifiHotspot for WiFi {}
pub struct OSX {
pub name: String,
interface: String,
}
impl OSX {
#[cfg(target_os = "windows")]
pub fn new(name: &str, interface: Option<&str>) -> Self {
OSX {
name: name.into(),
interface: interface.unwrap_or("en0").into(),
}
}
}
+2 -7
View File
@@ -2,15 +2,10 @@ use connectivity::handlers::NetworkXmlProfileHandler;
use connectivity::{ use connectivity::{
Network, WifiConnectionError, WifiError, WifiHotspot, WifiHotspotError, WifiInterface, Network, WifiConnectionError, WifiError, WifiHotspot, WifiHotspotError, WifiInterface,
}; };
use platforms::Windows; use platforms::WiFi;
use std::process::Command; use std::process::Command;
// #[derive(Debug)] impl WifiHotspot for WiFi {
// pub(crate) struct Windows {
// name: String,
// }
impl WifiHotspot for Windows {
fn create_hotspot(ssid: &str, password: &str) -> Result<bool, WifiHotspotError> { fn create_hotspot(ssid: &str, password: &str) -> Result<bool, WifiHotspotError> {
let output = Command::new("netsh") let output = Command::new("netsh")
.args(&[ .args(&[
+23 -11
View File
@@ -4,21 +4,33 @@ mod connectivity;
mod hotspot; mod hotspot;
mod platforms; mod platforms;
pub use connectivity::{Config, *}; pub mod prelude {
pub use hotspot::*; pub use connectivity::*;
pub use hotspot::*;
}
pub use platforms::*;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::prelude::*;
use super::*;
#[test] #[test]
fn connect_to_wifi_failed() { fn connect_to_wifi_failed() {
// let config = Some(Config { let config = Some(Config {
// interface: Some("wlo1"), interface: Some("wlo1"),
// }); });
// let wifi = WiFi::new("hello", config).unwrap(); let mut wifi = WiFi::new(config);
// assert_eq!(wifi.connect("password").unwrap(), false); assert_eq!(wifi.connect("ssid", "password").unwrap(), false);
} }
#[test]
fn create_hotspot() {
let hotspot_created = WiFi::create_hotspot("hello", "hi").unwrap();
assert_eq!(hotspot_created, true);
}
} }
+17 -16
View File
@@ -2,26 +2,27 @@ mod connectivity;
mod hotspot; mod hotspot;
mod platforms; mod platforms;
pub use connectivity::{Config, WifiConnectionError}; use connectivity::{Network, WifiConnectionError};
use platforms::{Config, WiFi};
fn main() -> Result<(), WifiConnectionError> { fn main() -> Result<(), WifiConnectionError> {
// let config = Some(Config { let config = Some(Config {
// interface: Some("wlo1"), interface: Some("wlo1"),
// }); });
// let wifi = WiFi::new("AndroidAPSD22", config); let mut wifi = WiFi::new(config);
// match wifi.connect("belm4235") { match wifi.connect("AndroidAPSD22", "belm4235") {
// Ok(result) => println!( Ok(result) => println!(
// "{}", "{}",
// if result == true { if result == true {
// "Connection Successfull." "Connection Successfull."
// } else { } else {
// "Invalid password." "Invalid password."
// } }
// ), ),
// Err(err) => println!("The following error occurred: {:?}", err), Err(err) => println!("The following error occurred: {:?}", err),
// } }
Ok(()) Ok(())
} }
+21 -23
View File
@@ -1,58 +1,56 @@
use platforms::{WifiError, WifiInterface}; use platforms::{Config, WifiError, WifiInterface};
use std::collections::HashMap;
use std::process::Command; use std::process::Command;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Connection { pub struct Connection {
pub ssid: String, pub(crate) ssid: String,
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Linux { pub struct Linux {
hotspot: Option<HashMap<String, String>>, pub(crate) connection: Option<Connection>,
pub connection: Option<Connection>, pub(crate) interface: String,
pub interface: String,
} }
impl Linux { impl Linux {
pub fn new(name: &str, interface: Option<&str>) -> Self { pub fn new(config: Option<Config>) -> Self {
Linux { Linux {
hotspot: None,
connection: None, connection: None,
interface: String::from("wlan0"), interface: config.map_or("wlan0".to_string(), |cfg| {
cfg.interface.unwrap_or("wlan0").to_string()
}),
} }
} }
} }
impl WifiInterface for Linux { impl WifiInterface for Linux {
fn is_wifi_enabled() -> bool { fn is_wifi_enabled() -> Result<bool, WifiError> {
let output = Command::new("nmcli").args(&["radio", "wifi"]).output(); let output = Command::new("nmcli")
.args(&["radio", "wifi"])
.output()
.map_err(|err| WifiError::IoError(err))?;
if let Err(_) = output { Ok(String::from_utf8_lossy(&output.stdout)
return false;
}
String::from_utf8_lossy(&output.unwrap().stdout)
.replace(" ", "") .replace(" ", "")
.replace("\n", "") .replace("\n", "")
.contains("enabled") .contains("enabled"))
} }
fn turn_on() -> Result<bool, WifiError> { fn turn_on() -> Result<(), WifiError> {
let _output = Command::new("nmcli") let _output = Command::new("nmcli")
.args(&["radio", "wifi", "on"]) .args(&["radio", "wifi", "on"])
.output() .output()
.map_err(|err| WifiError::IoError(err))?; .map_err(|err| WifiError::IoError(err))?;
Ok(true) Ok(())
} }
fn turn_off() -> Result<bool, WifiError> { fn turn_off() -> Result<(), WifiError> {
let _output = Command::new("nmcli") let _output = Command::new("nmcli")
.args(&["radio", "wifi", "off"]) .args(&["radio", "wifi", "off"])
.output() .output()
.map_err(|err| WifiError::IoError(err))?; .map_err(|err| WifiError::IoError(err))?;
Ok(true) Ok(())
} }
} }
+24 -13
View File
@@ -1,38 +1,49 @@
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
mod linux; mod linux;
#[cfg(target_os = "osx")] #[cfg(target_os = "osx")]
mod osx; mod osx;
#[cfg(target_os = "windows")]
// #[cfg(target_os = "windows")]
mod windows; mod windows;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub(crate) use self::linux::{Connection, Linux}; pub use self::linux::{Connection, Linux as WiFi};
#[cfg(target_os = "osx")] #[cfg(target_os = "osx")]
pub use self::osx::OSX; pub use self::osx::{Connection, Osx as WiFi};
#[cfg(target_os = "windows")]
// #[cfg(target_os = "windows")] pub use self::windows::{Connection, Windows as WiFi};
pub use self::windows::Windows;
use std::{fmt, io}; use std::{fmt, io};
/// Configuration for a wifi network.
#[derive(Debug, Clone)]
pub struct Config<'a> {
pub interface: Option<&'a str>,
}
#[derive(Debug)] #[derive(Debug)]
pub enum WifiError { pub enum WifiError {
OsNotSupported, // OsNotSupported,
InterfaceDisabled, InterfaceDisabled,
#[cfg(target_os = "windows")]
InterfaceFailedToOn, InterfaceFailedToOn,
IoError(io::Error), IoError(io::Error),
} }
pub trait WifiInterface: fmt::Debug { pub trait WifiInterface: fmt::Debug {
/// Checks if the wifi interface on host machine is enables. /// Checks if the wifi interface on host machine is enables.
fn is_wifi_enabled() -> bool; fn is_wifi_enabled() -> Result<bool, WifiError> {
unimplemented!();
}
/// Turns on the wifi interface of host machine. /// Turns on the wifi interface of host machine.
fn turn_on() -> Result<bool, WifiError>; fn turn_on() -> Result<(), WifiError> {
unimplemented!();
}
// Turns off the wifi interface of host machine. // Turns off the wifi interface of host machine.
fn turn_off() -> Result<bool, WifiError>; fn turn_off() -> Result<(), WifiError> {
unimplemented!();
}
} }
+52 -3
View File
@@ -1,7 +1,56 @@
use std::collections::HashMap; use platforms::{WifiError, WifiInterface};
use std::process::Command;
#[derive(Debug)]
pub struct Connection {
pub(crate) ssid: String,
}
#[derive(Debug)] #[derive(Debug)]
pub struct Osx { pub struct Osx {
hotspot: Option<HashMap>, pub(crate) connection: Option<Connection>,
connectivity: Option<HashMap>, pub(crate) interface: String,
}
impl Osx {
pub fn new(name: &str, config: Option<Config>) -> Self {
Osx {
connection: None,
interface: config.map_or("en0".to_string(), |cfg| {
cfg.interface.unwrap_or("en0").to_string()
}),
}
}
}
impl WifiInterface for Osx {
fn is_wifi_enabled() -> Result<bool, WifiError> {
let output = Command::new("networksetup")
.args(&["radio", "wifi"])
.output()
.map_err(|err| WifiError::IoError(err))?;
Ok(String::from_utf8_lossy(&output.stdout)
.replace(" ", "")
.replace("\n", "")
.contains("enabled"))
}
fn turn_on() -> Result<(), WifiError> {
let output = Command::new("networksetup")
.args(&["-setairportpower", "en0", "on"])
.output()
.map_err(|err| WifiError::IoError(err))?;
Ok(())
}
fn turn_off() -> Result<(), WifiError> {
let output = Command::new("networksetup")
.args(&["-setairportpower", "en0", "off"])
.output()
.map_err(|err| WifiError::IoError(err))?;
Ok(())
}
} }
+18 -14
View File
@@ -1,22 +1,26 @@
use platforms::{WifiError, WifiInterface};
use std::collections::HashMap; use std::collections::HashMap;
use
#[derive(Debug)]
pub struct Connection {
pub(crate) ssid: String,
}
#[derive(Debug)] #[derive(Debug)]
pub struct Windows { pub struct Windows {
hotspot: Option<HashMap>, pub(crate) connection: Option<Connection>,
connectivity: Option<HashMap>, pub(crate) interface: String,
} }
impl WifiInterface for Windows { impl Windows {
fn is_wifi_enabled() -> bool { pub fn new(name: &str, config: Option<Config>) -> Self {
unimplemented!() Windows {
} connection: None,
interface: config.map_or("wlan0".to_string(), |cfg| {
fn turn_on() -> Result<bool, WifiError> { cfg.interface.unwrap_or("wlan0").to_string()
unimplemented!() }),
} }
fn turn_off() -> Result<bool, WifiError> {
unimplemented!()
} }
} }
impl WifiInterface for Windows {}