use std::{collections::HashMap, fmt};
use js_sys::{JsString, Reflect};
use n0_future::time::Instant;
pub const BROWSER_INTERFACE: &str = "browserif";
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Interface {
is_up: bool,
}
impl fmt::Display for Interface {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "navigator.onLine={}", self.is_up)
}
}
impl Interface {
async fn new() -> Self {
let is_up = match Self::is_up() {
Some(v) => v,
None => {
tracing::warn!("navigator.onLine unavailable, assuming up");
true
}
};
tracing::debug!(onLine = is_up, "Fetched globalThis.navigator.onLine");
Self { is_up }
}
fn is_up() -> Option<bool> {
let navigator = Reflect::get(
js_sys::global().as_ref(),
JsString::from("navigator").as_ref(),
)
.ok()?;
let is_up = Reflect::get(&navigator, JsString::from("onLine").as_ref()).ok()?;
is_up.as_bool()
}
pub(crate) fn name(&self) -> &str {
BROWSER_INTERFACE
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct State {
pub interfaces: HashMap<String, Interface>,
pub have_v6: bool,
pub have_v4: bool,
pub(crate) is_expensive: bool,
pub(crate) default_route_interface: Option<String>,
pub(crate) http_proxy: Option<String>,
pub(crate) pac: Option<String>,
pub last_unsuspend: Option<Instant>,
}
impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for iface in self.interfaces.values() {
write!(f, "{iface}")?;
if let Some(ref default_if) = self.default_route_interface {
if iface.name() == default_if {
write!(f, " (default)")?;
}
}
if f.alternate() {
writeln!(f)?;
} else {
write!(f, "; ")?;
}
}
Ok(())
}
}
impl State {
pub async fn new() -> Self {
let mut interfaces = HashMap::new();
let have_v6 = false;
let have_v4 = false;
interfaces.insert(BROWSER_INTERFACE.to_string(), Interface::new().await);
State {
interfaces,
have_v4,
have_v6,
is_expensive: false,
default_route_interface: Some(BROWSER_INTERFACE.to_string()),
http_proxy: None,
pac: None,
last_unsuspend: None,
}
}
pub fn is_major_change(&self, old: &State) -> bool {
self != old
}
}