use crate::{
measurements::{Measurement, SuccessfulMeasurement},
SystemStatus,
};
pub struct NetworkConnectedDevice {
ip_address: std::net::IpAddr,
port: u16,
use_tls: bool,
}
pub struct TcpPort(u16);
impl From<u16> for TcpPort {
fn from(value: u16) -> Self {
TcpPort(value)
}
}
impl Default for TcpPort {
fn default() -> Self {
80.into()
}
}
pub enum UseTls {
Off,
On,
Auto,
}
impl From<bool> for UseTls {
fn from(value: bool) -> Self {
if value {
return UseTls::On;
} else {
return UseTls::Off;
}
}
}
impl Default for UseTls {
fn default() -> Self {
UseTls::Auto
}
}
pub fn connect_via_network(
ip_address: std::net::IpAddr,
use_tls: UseTls,
) -> NetworkConnectedDevice {
connect_via_network_on_port(ip_address, 80.into(), use_tls)
}
pub fn connect_via_network_on_port(
ip_address: std::net::IpAddr,
port: TcpPort,
use_tls: UseTls,
) -> NetworkConnectedDevice {
NetworkConnectedDevice {
ip_address,
port: port.0,
use_tls: match use_tls {
UseTls::Off => false,
UseTls::On => true,
UseTls::Auto => port.0 == 443,
},
}
}
impl NetworkConnectedDevice {
fn base_url(&self) -> String {
format!(
"{proto}://{addr}:{port}",
proto = if self.use_tls { "https" } else { "http" },
addr = self.ip_address.to_string(),
port = self.port,
)
}
}
async fn fetch<T: serde::de::DeserializeOwned>(
device: &NetworkConnectedDevice,
url: &str,
) -> Result<T, crate::Error> {
let response = reqwest::get(device.base_url() + url)
.await
.map_err(|err| crate::Error::Network(err))?;
let response = response
.text()
.await
.map_err(|err| crate::Error::Network(err))?;
serde_json::from_str::<T>(response.as_str())
.map_err(|err| crate::Error::InvalidJsonResponse(err))
}
impl super::Device for NetworkConnectedDevice {
async fn latest_measurement(&self) -> Result<crate::measurements::Measurement, crate::Error> {
fetch::<Measurement>(&self, "/api/v1/measurements/latest").await
}
async fn latest_successful_measurement(
&self,
) -> Result<Option<crate::measurements::SuccessfulMeasurement>, crate::Error> {
fetch::<Option<SuccessfulMeasurement>>(&self, "/api/v1/measurements/latest_successful")
.await
}
async fn system_status(&self) -> Result<SystemStatus, crate::Error> {
fetch::<SystemStatus>(&self, "/api/v1/system/status").await
}
}