use std::time::{Duration, Instant};
use aranet_core::settings::{DeviceSettings, RadonUnit, TemperatureUnit};
pub const TOAST_DURATION: Duration = Duration::from_secs(4);
pub const SCAN_DURATION: Duration = Duration::from_secs(5);
pub const INTERVAL_OPTIONS: &[(u16, &str)] = &[
(60, "1 min"),
(120, "2 min"),
(300, "5 min"),
(600, "10 min"),
];
#[derive(Debug, Clone)]
#[allow(dead_code)] pub enum ToastType {
Success,
Error,
Info,
}
#[derive(Debug, Clone)]
pub struct Toast {
pub message: String,
pub toast_type: ToastType,
pub created_at: Instant,
}
impl Toast {
pub fn new(message: impl Into<String>, toast_type: ToastType) -> Self {
Self {
message: message.into(),
toast_type,
created_at: Instant::now(),
}
}
pub fn is_expired(&self) -> bool {
self.created_at.elapsed() > TOAST_DURATION
}
}
#[inline]
pub fn celsius_to_fahrenheit(celsius: f32) -> f32 {
celsius * 9.0 / 5.0 + 32.0
}
#[inline]
pub fn bq_to_pci(bq: u32) -> f32 {
bq as f32 * 0.027
}
#[inline]
pub fn hpa_to_inhg(hpa: f32) -> f32 {
hpa * 0.02953
}
pub fn format_temperature(
celsius: f32,
settings: Option<&DeviceSettings>,
app_preference: Option<&str>,
) -> (String, &'static str) {
let use_fahrenheit = settings
.map(|s| s.temperature_unit == TemperatureUnit::Fahrenheit)
.unwrap_or_else(|| app_preference == Some("fahrenheit"));
if use_fahrenheit {
(format!("{:.1}", celsius_to_fahrenheit(celsius)), "°F")
} else {
(format!("{:.1}", celsius), "°C")
}
}
pub fn format_pressure(hpa: f32, app_preference: &str) -> (String, &'static str) {
if app_preference == "inhg" {
(format!("{:.2}", hpa_to_inhg(hpa)), "inHg")
} else {
(format!("{:.1}", hpa), "hPa")
}
}
pub fn format_radon(bq: u32, settings: Option<&DeviceSettings>) -> (String, &'static str) {
let use_pci = settings
.map(|s| s.radon_unit == RadonUnit::PciL)
.unwrap_or(false);
if use_pci {
(format!("{:.2}", bq_to_pci(bq)), "pCi/L")
} else {
(format!("{}", bq), "Bq/m3")
}
}
pub fn format_uptime(seconds: u64) -> String {
if seconds < 60 {
format!("{}s", seconds)
} else if seconds < 3600 {
format!("{}m {}s", seconds / 60, seconds % 60)
} else if seconds < 86400 {
let hours = seconds / 3600;
let mins = (seconds % 3600) / 60;
format!("{}h {}m", hours, mins)
} else {
let days = seconds / 86400;
let hours = (seconds % 86400) / 3600;
format!("{}d {}h", days, hours)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_celsius_to_fahrenheit_freezing() {
let result = celsius_to_fahrenheit(0.0);
assert!((result - 32.0).abs() < 0.01);
}
#[test]
fn test_celsius_to_fahrenheit_boiling() {
let result = celsius_to_fahrenheit(100.0);
assert!((result - 212.0).abs() < 0.01);
}
#[test]
fn test_celsius_to_fahrenheit_room_temp() {
let result = celsius_to_fahrenheit(20.0);
assert!((result - 68.0).abs() < 0.01);
}
#[test]
fn test_celsius_to_fahrenheit_negative() {
let result = celsius_to_fahrenheit(-40.0);
assert!((result - (-40.0)).abs() < 0.01);
}
#[test]
fn test_bq_to_pci_zero() {
let result = bq_to_pci(0);
assert!((result - 0.0).abs() < 0.001);
}
#[test]
fn test_bq_to_pci_100() {
let result = bq_to_pci(100);
assert!((result - 2.7).abs() < 0.01);
}
#[test]
fn test_bq_to_pci_who_action_level() {
let result = bq_to_pci(100);
assert!((result - 2.7).abs() < 0.1);
}
#[test]
fn test_format_temperature_no_settings_defaults_celsius() {
let (value, unit) = format_temperature(20.5, None, None);
assert_eq!(value, "20.5");
assert_eq!(unit, "°C");
}
#[test]
fn test_format_temperature_celsius_setting() {
let settings = DeviceSettings {
temperature_unit: TemperatureUnit::Celsius,
..Default::default()
};
let (value, unit) = format_temperature(20.5, Some(&settings), None);
assert_eq!(value, "20.5");
assert_eq!(unit, "°C");
}
#[test]
fn test_format_temperature_fahrenheit_setting() {
let settings = DeviceSettings {
temperature_unit: TemperatureUnit::Fahrenheit,
..Default::default()
};
let (value, unit) = format_temperature(20.0, Some(&settings), None);
assert_eq!(value, "68.0");
assert_eq!(unit, "°F");
}
#[test]
fn test_format_temperature_fahrenheit_decimal() {
let settings = DeviceSettings {
temperature_unit: TemperatureUnit::Fahrenheit,
..Default::default()
};
let (value, unit) = format_temperature(21.5, Some(&settings), None);
assert_eq!(value, "70.7");
assert_eq!(unit, "°F");
}
#[test]
fn test_format_temperature_app_preference_fahrenheit() {
let (value, unit) = format_temperature(20.0, None, Some("fahrenheit"));
assert_eq!(value, "68.0");
assert_eq!(unit, "°F");
}
#[test]
fn test_format_temperature_device_overrides_app_preference() {
let settings = DeviceSettings {
temperature_unit: TemperatureUnit::Celsius,
..Default::default()
};
let (value, unit) = format_temperature(20.0, Some(&settings), Some("fahrenheit"));
assert_eq!(value, "20.0");
assert_eq!(unit, "°C");
}
#[test]
fn test_format_pressure_hpa() {
let (value, unit) = format_pressure(1013.25, "hpa");
assert_eq!(value, "1013.2");
assert_eq!(unit, "hPa");
}
#[test]
fn test_format_pressure_inhg() {
let (value, unit) = format_pressure(1013.25, "inhg");
assert_eq!(unit, "inHg");
let val: f32 = value.parse().unwrap();
assert!((val - 29.92).abs() < 0.1);
}
#[test]
fn test_hpa_to_inhg_standard_atm() {
let result = hpa_to_inhg(1013.25);
assert!((result - 29.92).abs() < 0.1);
}
#[test]
fn test_format_radon_no_settings_defaults_bq() {
let (value, unit) = format_radon(100, None);
assert_eq!(value, "100");
assert_eq!(unit, "Bq/m3");
}
#[test]
fn test_format_radon_bq_setting() {
let settings = DeviceSettings {
radon_unit: RadonUnit::BqM3,
..Default::default()
};
let (value, unit) = format_radon(100, Some(&settings));
assert_eq!(value, "100");
assert_eq!(unit, "Bq/m3");
}
#[test]
fn test_format_radon_pci_setting() {
let settings = DeviceSettings {
radon_unit: RadonUnit::PciL,
..Default::default()
};
let (value, unit) = format_radon(100, Some(&settings));
assert_eq!(value, "2.70");
assert_eq!(unit, "pCi/L");
}
#[test]
fn test_format_radon_pci_zero() {
let settings = DeviceSettings {
radon_unit: RadonUnit::PciL,
..Default::default()
};
let (value, unit) = format_radon(0, Some(&settings));
assert_eq!(value, "0.00");
assert_eq!(unit, "pCi/L");
}
#[test]
fn test_format_radon_pci_high_value() {
let settings = DeviceSettings {
radon_unit: RadonUnit::PciL,
..Default::default()
};
let (value, unit) = format_radon(300, Some(&settings));
assert_eq!(value, "8.10");
assert_eq!(unit, "pCi/L");
}
}