359 lines
11 KiB
Rust
359 lines
11 KiB
Rust
use std::fmt::Display;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
current_weather_output_model::CurrentWeatherOutputModel,
|
|
formats::data_format::DataFormat,
|
|
formats::speed_unit::{speed_to_unit_string, SpeedUnit},
|
|
formats::temp_unit::{temp_to_unit_string, TempUnit},
|
|
};
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct CurrentWeatherOutput {
|
|
pub format: DataFormat,
|
|
pub temperature_unit: TempUnit,
|
|
pub speed_unit: SpeedUnit,
|
|
|
|
pub data: CurrentWeatherOutputModel,
|
|
}
|
|
|
|
impl Display for CurrentWeatherOutput {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
if DataFormat::JSON == self.format {
|
|
write!(f, "{}", serde_json::to_string(&self.data).unwrap())
|
|
} else {
|
|
let mut string_vec: Vec<String> = Vec::new();
|
|
|
|
if DataFormat::Normal == self.format {
|
|
string_vec.push(self.create_header());
|
|
} else {
|
|
if let Some(latitude) = self.data.latitude {
|
|
string_vec.push(latitude.to_string())
|
|
}
|
|
if let Some(longitude) = self.data.longitude {
|
|
string_vec.push(longitude.to_string())
|
|
}
|
|
if let Some(city) = &self.data.city {
|
|
string_vec.push(city.to_string())
|
|
}
|
|
}
|
|
|
|
let is_day = self.extract_day();
|
|
if let Some(is_day) = is_day {
|
|
string_vec.push(is_day);
|
|
}
|
|
|
|
let temperature = self.extract_temperature();
|
|
if let Some(temperature) = temperature {
|
|
string_vec.push(temperature);
|
|
}
|
|
|
|
let windspeed = self.extract_wind_speed();
|
|
if let Some(windspeed) = windspeed {
|
|
string_vec.push(windspeed);
|
|
}
|
|
|
|
let winddirection = self.extract_wind_direction();
|
|
if let Some(winddirection) = winddirection {
|
|
string_vec.push(winddirection);
|
|
}
|
|
|
|
if DataFormat::Normal == self.format
|
|
&& self.data.latitude.is_some()
|
|
&& self.data.longitude.is_some()
|
|
{
|
|
string_vec.push(format!(
|
|
"{}, {}",
|
|
self.parse_simple_data(
|
|
&self.data.latitude.unwrap().to_string(),
|
|
"Latitude",
|
|
None
|
|
),
|
|
self.parse_simple_data(
|
|
&self.data.longitude.unwrap().to_string(),
|
|
"Longitude",
|
|
None
|
|
)
|
|
));
|
|
}
|
|
|
|
if DataFormat::Clean == self.format {
|
|
write!(f, "{}", string_vec.join(","))
|
|
} else {
|
|
string_vec.push(self.create_footer());
|
|
write!(f, "{}", string_vec.join("\n"))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CurrentWeatherOutput {
|
|
pub fn new(
|
|
format: DataFormat,
|
|
temperature_unit: TempUnit,
|
|
speed_unit: SpeedUnit,
|
|
) -> CurrentWeatherOutput {
|
|
CurrentWeatherOutput {
|
|
format, temperature_unit, speed_unit,
|
|
data: CurrentWeatherOutputModel::new(),
|
|
}
|
|
}
|
|
|
|
fn create_header(&self) -> String {
|
|
let mut title_header: Vec<String> = Vec::new();
|
|
title_header.push(String::from("=== Current weather"));
|
|
|
|
if self.data.city.is_some() {
|
|
title_header.push(String::from("for"));
|
|
title_header.push(self.data.city.clone().unwrap());
|
|
}
|
|
|
|
title_header.push(String::from("==="));
|
|
title_header.join(" ")
|
|
}
|
|
|
|
fn create_footer(&self) -> String {
|
|
String::from("=== Weather data by Open-Meteo.com ===")
|
|
}
|
|
|
|
fn extract_day(&self) -> Option<String> {
|
|
if self.data.is_day.is_some() {
|
|
let day = self.data.is_day.unwrap();
|
|
if day == 1 {
|
|
Some(self.parse_custom_data(&day.to_string(), "Day"))
|
|
} else {
|
|
Some(self.parse_custom_data(&day.to_string(), "Night"))
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn extract_temperature(&self) -> Option<String> {
|
|
if self.data.temperature.is_some() {
|
|
let temperature = self.data.temperature.unwrap();
|
|
Some(self.parse_simple_data(
|
|
&temperature.to_string(),
|
|
"Temperature",
|
|
Some(temp_to_unit_string(&self.temperature_unit).as_str()),
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn extract_wind_speed(&self) -> Option<String> {
|
|
if self.data.windspeed.is_some() {
|
|
let windspeed = self.data.windspeed.unwrap();
|
|
Some(self.parse_simple_data(
|
|
&windspeed.to_string(),
|
|
"Wind speed",
|
|
Some(format!(" {}", speed_to_unit_string(&self.speed_unit)).as_str()),
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn extract_wind_direction(&self) -> Option<String> {
|
|
if self.data.winddirection.is_some() {
|
|
let winddirection = self.data.winddirection.unwrap();
|
|
Some(self.parse_simple_data(&winddirection.to_string(), "Wind direction", Some("°")))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn parse_custom_data(&self, data: &str, custom: &str) -> String {
|
|
if self.format == DataFormat::Clean {
|
|
data.to_string()
|
|
} else {
|
|
custom.to_string()
|
|
}
|
|
}
|
|
|
|
fn parse_simple_data(&self, data: &str, descriptor: &str, end_text: Option<&str>) -> String {
|
|
if self.format == DataFormat::Clean {
|
|
data.to_string()
|
|
} else {
|
|
let end_text = end_text.unwrap_or("");
|
|
format!("{descriptor}: {data}{end_text}")
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::formats::speed_unit::SpeedUnit;
|
|
use crate::formats::temp_unit::TempUnit;
|
|
|
|
#[test]
|
|
fn clean_all_data() {
|
|
let data = CurrentWeatherOutputModel {
|
|
latitude: Some(5.0),
|
|
longitude: Some(-5.0),
|
|
city: Some("TestCity".to_string()),
|
|
is_day: Some(1),
|
|
temperature: Some(12.5),
|
|
windspeed: Some(7.0),
|
|
winddirection: Some(90.0),
|
|
};
|
|
|
|
let output = CurrentWeatherOutput {
|
|
format: DataFormat::Clean,
|
|
temperature_unit: TempUnit::Celsius,
|
|
speed_unit: SpeedUnit::Kmh,
|
|
data,
|
|
};
|
|
|
|
assert_eq!(output.to_string(), "5,-5,TestCity,1,12.5,7,90");
|
|
}
|
|
|
|
#[test]
|
|
fn clean_data() {
|
|
let data = CurrentWeatherOutputModel {
|
|
latitude: None,
|
|
longitude: None,
|
|
city: None,
|
|
|
|
is_day: Some(1),
|
|
temperature: Some(15.5),
|
|
windspeed: Some(12.2),
|
|
winddirection: None,
|
|
};
|
|
|
|
let output = CurrentWeatherOutput {
|
|
format: DataFormat::Clean,
|
|
temperature_unit: TempUnit::Celsius,
|
|
speed_unit: SpeedUnit::Kmh,
|
|
data,
|
|
};
|
|
|
|
assert_eq!(output.to_string(), "1,15.5,12.2");
|
|
}
|
|
|
|
#[test]
|
|
fn full_normal_data() {
|
|
let data = CurrentWeatherOutputModel {
|
|
latitude: Some(5.0),
|
|
longitude: Some(-5.0),
|
|
city: Some("TestCity".to_string()),
|
|
|
|
is_day: Some(0),
|
|
temperature: Some(22.0),
|
|
windspeed: Some(15.5),
|
|
winddirection: Some(118.0),
|
|
};
|
|
|
|
let output = CurrentWeatherOutput {
|
|
format: DataFormat::Normal,
|
|
temperature_unit: TempUnit::Celsius,
|
|
speed_unit: SpeedUnit::Kmh,
|
|
data,
|
|
};
|
|
|
|
let result = output.to_string();
|
|
assert!(result.contains("Night"));
|
|
assert!(result.contains("Temperature: 22°C"));
|
|
assert!(result.contains("Wind speed: 15.5 km/h"));
|
|
assert!(result.contains("Wind direction: 118°"));
|
|
assert!(result.contains("Latitude: 5"));
|
|
assert!(result.contains("Longitude: -5"));
|
|
assert!(result.contains("TestCity"));
|
|
assert!(result.contains("Open-Meteo.com"));
|
|
}
|
|
|
|
#[test]
|
|
fn normal_data() {
|
|
let data = CurrentWeatherOutputModel {
|
|
latitude: None,
|
|
longitude: None,
|
|
city: None,
|
|
|
|
is_day: Some(1),
|
|
temperature: Some(55.0),
|
|
windspeed: Some(11.5),
|
|
winddirection: None,
|
|
};
|
|
|
|
let output = CurrentWeatherOutput {
|
|
format: DataFormat::Normal,
|
|
temperature_unit: TempUnit::Fahrenheit,
|
|
speed_unit: SpeedUnit::Mph,
|
|
data,
|
|
};
|
|
|
|
let result = output.to_string();
|
|
assert!(result.contains("Day"));
|
|
assert!(result.contains("Temperature: 55°F"));
|
|
assert!(result.contains("Wind speed: 11.5 mp/h"));
|
|
assert!(!result.contains("Wind direction: 125°"));
|
|
assert!(!result.contains("Latitude: 12.15"));
|
|
assert!(!result.contains("Longitude: 0.235"));
|
|
assert!(!result.contains("Nocity"));
|
|
assert!(result.contains("Open-Meteo.com"));
|
|
}
|
|
|
|
#[test]
|
|
fn full_json_data() {
|
|
let data = CurrentWeatherOutputModel {
|
|
latitude: Some(5.0),
|
|
longitude: Some(-5.0),
|
|
city: Some("TestCity".to_string()),
|
|
|
|
is_day: Some(0),
|
|
temperature: Some(22.0),
|
|
windspeed: Some(15.5),
|
|
winddirection: Some(118.0),
|
|
};
|
|
|
|
let output = CurrentWeatherOutput {
|
|
format: DataFormat::JSON,
|
|
temperature_unit: TempUnit::Celsius,
|
|
speed_unit: SpeedUnit::Kmh,
|
|
data,
|
|
};
|
|
|
|
let result = output.to_string();
|
|
assert!(result.contains("\"latitude\":5"));
|
|
assert!(result.contains("\"longitude\":-5"));
|
|
assert!(result.contains("\"city\":\"TestCity\""));
|
|
assert!(result.contains("\"is_day\":0"));
|
|
assert!(result.contains("\"temperature\":22"));
|
|
assert!(result.contains("\"windspeed\":15.5"));
|
|
assert!(result.contains("\"winddirection\":118"));
|
|
}
|
|
|
|
#[test]
|
|
fn json_data() {
|
|
let data = CurrentWeatherOutputModel {
|
|
latitude: None,
|
|
longitude: None,
|
|
city: None,
|
|
|
|
is_day: Some(1),
|
|
temperature: Some(55.0),
|
|
windspeed: Some(11.5),
|
|
winddirection: None,
|
|
};
|
|
|
|
let output = CurrentWeatherOutput {
|
|
format: DataFormat::JSON,
|
|
temperature_unit: TempUnit::Fahrenheit,
|
|
speed_unit: SpeedUnit::Mph,
|
|
data,
|
|
};
|
|
|
|
let result = output.to_string();
|
|
assert!(!result.contains("\"latitude\":12.15"));
|
|
assert!(!result.contains("\"longitude\":-0.235"));
|
|
assert!(!result.contains("\"city\":\"NoCity\""));
|
|
assert!(result.contains("\"is_day\":1"));
|
|
assert!(result.contains("\"temperature\":55"));
|
|
assert!(result.contains("\"windspeed\":11.5"));
|
|
assert!(!result.contains("\"winddirection\":125"));
|
|
}
|
|
}
|