use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TemperatureUnit {
#[default]
Fahrenheit,
Celsius,
}
impl TemperatureUnit {
pub fn symbol(&self) -> &'static str {
match self {
Self::Fahrenheit => "°F",
Self::Celsius => "°C",
}
}
pub fn api_param(&self) -> &'static str {
match self {
Self::Fahrenheit => "fahrenheit",
Self::Celsius => "celsius",
}
}
pub fn format(&self, temp: f32) -> String {
format!("{:.0}{}", temp, self.symbol())
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PressureUnit {
#[default]
Hpa,
InHg,
Psi,
}
impl PressureUnit {
pub fn symbol(&self) -> &'static str {
match self {
Self::Hpa => "hPa",
Self::InHg => "inHg",
Self::Psi => "PSI",
}
}
pub fn convert(&self, hpa: f32) -> f32 {
match self {
Self::Hpa => hpa,
Self::InHg => hpa / 33.8639,
Self::Psi => hpa / 68.9476,
}
}
pub fn format(&self, hpa: f32) -> String {
let value = self.convert(hpa);
match self {
Self::Hpa => format!("{:.0} {}", value, self.symbol()),
Self::InHg => format!("{:.2} {}", value, self.symbol()),
Self::Psi => format!("{:.1} {}", value, self.symbol()),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MeasurementSystem {
#[default]
Imperial,
Metric,
}
impl MeasurementSystem {
pub fn wind_speed_unit(&self) -> &'static str {
match self {
Self::Imperial => "mph",
Self::Metric => "km/h",
}
}
pub fn visibility_unit(&self) -> &'static str {
match self {
Self::Imperial => "mi",
Self::Metric => "km",
}
}
pub fn precipitation_unit(&self) -> &'static str {
match self {
Self::Imperial => "in",
Self::Metric => "mm",
}
}
pub fn wind_speed_api_param(&self) -> &'static str {
match self {
Self::Imperial => "mph",
Self::Metric => "kmh",
}
}
pub fn precipitation_api_param(&self) -> &'static str {
match self {
Self::Imperial => "inch",
Self::Metric => "mm",
}
}
pub fn convert_visibility(&self, meters: f32) -> f32 {
match self {
Self::Imperial => meters / 1609.34,
Self::Metric => meters / 1000.0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pressure_hpa_passthrough() {
assert_eq!(PressureUnit::Hpa.convert(1013.25), 1013.25);
}
#[test]
fn pressure_converts_to_inhg_and_psi() {
assert!((PressureUnit::InHg.convert(1013.25) - 29.92).abs() < 0.01);
assert!((PressureUnit::Psi.convert(1013.25) - 14.70).abs() < 0.01);
}
#[test]
fn pressure_format_uses_unit_precision() {
assert_eq!(PressureUnit::Hpa.format(1013.25), "1013 hPa");
assert_eq!(PressureUnit::InHg.format(1013.25), "29.92 inHg");
assert_eq!(PressureUnit::Psi.format(1013.25), "14.7 PSI");
}
#[test]
fn visibility_converts_from_meters() {
assert!((MeasurementSystem::Imperial.convert_visibility(1609.34) - 1.0).abs() < 0.001);
assert!((MeasurementSystem::Metric.convert_visibility(1000.0) - 1.0).abs() < 0.001);
}
#[test]
fn temperature_format_and_symbol() {
assert_eq!(TemperatureUnit::Fahrenheit.format(72.4), "72°F");
assert_eq!(TemperatureUnit::Celsius.format(22.6), "23°C");
assert_eq!(TemperatureUnit::Fahrenheit.symbol(), "°F");
}
#[test]
fn precipitation_unit_labels() {
assert_eq!(MeasurementSystem::Imperial.precipitation_unit(), "in");
assert_eq!(MeasurementSystem::Metric.precipitation_unit(), "mm");
}
#[test]
fn api_params_match_open_meteo() {
assert_eq!(TemperatureUnit::Fahrenheit.api_param(), "fahrenheit");
assert_eq!(TemperatureUnit::Celsius.api_param(), "celsius");
assert_eq!(MeasurementSystem::Imperial.wind_speed_api_param(), "mph");
assert_eq!(MeasurementSystem::Metric.wind_speed_api_param(), "kmh");
assert_eq!(
MeasurementSystem::Imperial.precipitation_api_param(),
"inch"
);
assert_eq!(MeasurementSystem::Metric.precipitation_api_param(), "mm");
}
}