use std::net::{TcpStream, ToSocketAddrs};
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NetworkStatus {
Online,
Offline,
Unknown,
}
impl NetworkStatus {
pub fn is_online(self) -> bool {
self == NetworkStatus::Online
}
pub fn is_offline(self) -> bool {
self == NetworkStatus::Offline
}
}
impl std::fmt::Display for NetworkStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NetworkStatus::Online => write!(f, "online"),
NetworkStatus::Offline => write!(f, "offline"),
NetworkStatus::Unknown => write!(f, "unknown"),
}
}
}
const CONNECT_TIMEOUT: Duration = Duration::from_secs(2);
const PROBE_TARGETS: &[&str] = &["1.1.1.1:443", "8.8.8.8:443"];
pub fn check_connectivity() -> NetworkStatus {
check_connectivity_with_timeout(CONNECT_TIMEOUT)
}
pub fn check_connectivity_with_timeout(timeout: Duration) -> NetworkStatus {
for target in PROBE_TARGETS {
match target.to_socket_addrs() {
Ok(mut addrs) => {
if let Some(addr) = addrs.next() {
if TcpStream::connect_timeout(&addr, timeout).is_ok() {
return NetworkStatus::Online;
}
}
}
Err(_) => continue,
}
}
NetworkStatus::Offline
}
pub fn is_online() -> bool {
check_connectivity().is_online()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn network_status_display() {
assert_eq!(NetworkStatus::Online.to_string(), "online");
assert_eq!(NetworkStatus::Offline.to_string(), "offline");
assert_eq!(NetworkStatus::Unknown.to_string(), "unknown");
}
#[test]
fn network_status_predicates() {
assert!(NetworkStatus::Online.is_online());
assert!(!NetworkStatus::Online.is_offline());
assert!(NetworkStatus::Offline.is_offline());
assert!(!NetworkStatus::Offline.is_online());
assert!(!NetworkStatus::Unknown.is_online());
assert!(!NetworkStatus::Unknown.is_offline());
}
#[test]
fn check_connectivity_returns_valid_status() {
let status = check_connectivity();
assert!(
status == NetworkStatus::Online
|| status == NetworkStatus::Offline
|| status == NetworkStatus::Unknown
);
}
#[test]
fn check_connectivity_with_zero_timeout_returns_offline() {
let status = check_connectivity_with_timeout(Duration::from_nanos(1));
assert!(
status == NetworkStatus::Offline || status == NetworkStatus::Online,
"should be offline or online, got {:?}",
status
);
}
#[test]
fn is_online_returns_bool() {
let _result: bool = is_online();
}
}