Adding format to select between normal, clean and JSON. This needs some refactor
This commit is contained in:
parent
9978e1553b
commit
8a3e5783c2
17
src/cli.rs
17
src/cli.rs
@ -1,6 +1,6 @@
|
|||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::{temp_unit::TempUnit, speed_unit::SpeedUnit};
|
use crate::{temp_unit::TempUnit, speed_unit::SpeedUnit, data_format::DataFormat};
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(
|
#[structopt(
|
||||||
@ -30,7 +30,7 @@ pub enum Arguments {
|
|||||||
// Flags
|
// Flags
|
||||||
#[structopt(short = "a", long, help = "Displays all the information")]
|
#[structopt(short = "a", long, help = "Displays all the information")]
|
||||||
all: bool,
|
all: bool,
|
||||||
#[structopt(short = "d", long, help = "Displays if it's day or night")]
|
#[structopt(short = "d", long, help = "Displays if it is day or night")]
|
||||||
is_day: bool,
|
is_day: bool,
|
||||||
#[structopt(short = "t", long, help = "Displays the decimal temperature")]
|
#[structopt(short = "t", long, help = "Displays the decimal temperature")]
|
||||||
temperature: bool,
|
temperature: bool,
|
||||||
@ -51,12 +51,23 @@ pub enum Arguments {
|
|||||||
#[structopt(long = "city", help = "Displays the city")]
|
#[structopt(long = "city", help = "Displays the city")]
|
||||||
include_city: bool,
|
include_city: bool,
|
||||||
|
|
||||||
|
#[structopt(long,
|
||||||
|
possible_values = &DataFormat::variants(), default_value = "Normal" , case_insensitive = true,
|
||||||
|
help = "Switches data format between Normal, Clean or JSON")]
|
||||||
|
format: DataFormat,
|
||||||
|
|
||||||
#[structopt(
|
#[structopt(
|
||||||
short = "c",
|
short = "c",
|
||||||
long,
|
long,
|
||||||
help = "Cleans the output and only displays the values separated by commas.
|
help = "Displays the output separated by commas. Same as '--format clean'
|
||||||
- ORDER: latitude, longitude, city, is_day, temperature, windspeed, winddirection"
|
- ORDER: latitude, longitude, city, is_day, temperature, windspeed, winddirection"
|
||||||
)]
|
)]
|
||||||
clean: bool,
|
clean: bool,
|
||||||
|
#[structopt(
|
||||||
|
short = "j",
|
||||||
|
long,
|
||||||
|
help = "Displays the output as JSON. Same as '--format json'"
|
||||||
|
)]
|
||||||
|
json: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
60
src/current_weather_extractor.rs
Normal file
60
src/current_weather_extractor.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use crate::current_weather_output::CurrentWeatherOutput;
|
||||||
|
use crate::{
|
||||||
|
coords::Coordinates, current_weather::CurrentWeather,
|
||||||
|
current_weather_print_params::CurrentWeatherPrintParams,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CurrentWeatherExtractor {
|
||||||
|
pub params: CurrentWeatherPrintParams,
|
||||||
|
|
||||||
|
current_weather: CurrentWeather,
|
||||||
|
coords: Coordinates,
|
||||||
|
city: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CurrentWeatherExtractor {
|
||||||
|
pub fn new(
|
||||||
|
current_weather: CurrentWeather,
|
||||||
|
params: CurrentWeatherPrintParams,
|
||||||
|
coords: Coordinates,
|
||||||
|
city: Option<String>,
|
||||||
|
) -> CurrentWeatherExtractor {
|
||||||
|
CurrentWeatherExtractor {
|
||||||
|
current_weather,
|
||||||
|
params,
|
||||||
|
coords,
|
||||||
|
city,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_output(&self) -> CurrentWeatherOutput {
|
||||||
|
let mut output = CurrentWeatherOutput::new(
|
||||||
|
self.params.format,
|
||||||
|
self.params.temperature_unit,
|
||||||
|
self.params.speed_unit,
|
||||||
|
);
|
||||||
|
|
||||||
|
if self.params.all || self.params.include_coords {
|
||||||
|
output.data.latitude = Some(self.coords.latitude);
|
||||||
|
output.data.longitude = Some(self.coords.longitude);
|
||||||
|
}
|
||||||
|
if self.params.all || self.params.include_city {
|
||||||
|
output.data.city = self.city.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.params.is_day || self.params.all {
|
||||||
|
output.data.is_day = Some(self.current_weather.is_day);
|
||||||
|
}
|
||||||
|
if self.params.temperature || self.params.all {
|
||||||
|
output.data.temperature = Some(self.current_weather.temperature);
|
||||||
|
}
|
||||||
|
if self.params.windspeed || self.params.all {
|
||||||
|
output.data.windspeed = Some(self.current_weather.windspeed);
|
||||||
|
}
|
||||||
|
if self.params.winddirection || self.params.all {
|
||||||
|
output.data.winddirection = Some(self.current_weather.winddirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
357
src/current_weather_output.rs
Normal file
357
src/current_weather_output.rs
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
current_weather_output_model::CurrentWeatherOutputModel,
|
||||||
|
data_format::DataFormat,
|
||||||
|
speed_unit::{speed_to_unit_string, SpeedUnit},
|
||||||
|
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 CurrentWeatherOutput {
|
||||||
|
pub fn new(
|
||||||
|
format: DataFormat,
|
||||||
|
temperature_unit: TempUnit,
|
||||||
|
speed_unit: SpeedUnit,
|
||||||
|
) -> CurrentWeatherOutput {
|
||||||
|
CurrentWeatherOutput {
|
||||||
|
format,
|
||||||
|
temperature_unit,
|
||||||
|
speed_unit,
|
||||||
|
data: CurrentWeatherOutputModel::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
if DataFormat::JSON == self.format {
|
||||||
|
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 self.data.latitude.is_some() {
|
||||||
|
string_vec.push(self.data.latitude.unwrap().to_string())
|
||||||
|
}
|
||||||
|
if self.data.longitude.is_some() {
|
||||||
|
string_vec.push(self.data.longitude.unwrap().to_string())
|
||||||
|
}
|
||||||
|
if self.data.city.is_some() {
|
||||||
|
string_vec.push(self.data.city.clone().unwrap().to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_day = self.extract_day();
|
||||||
|
if is_day.is_some() {
|
||||||
|
string_vec.push(is_day.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let temperature = self.extract_temperature();
|
||||||
|
if temperature.is_some() {
|
||||||
|
string_vec.push(temperature.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let windspeed = self.extract_wind_speed();
|
||||||
|
if windspeed.is_some() {
|
||||||
|
string_vec.push(windspeed.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let winddirection = self.extract_wind_direction();
|
||||||
|
if winddirection.is_some() {
|
||||||
|
string_vec.push(winddirection.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
let final_string = string_vec.join(",");
|
||||||
|
final_string
|
||||||
|
} else {
|
||||||
|
string_vec.push(self.create_footer());
|
||||||
|
string_vec.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
format!("{data}")
|
||||||
|
} else {
|
||||||
|
format!("{custom}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_simple_data(&self, data: &str, descriptor: &str, end_text: Option<&str>) -> String {
|
||||||
|
if self.format == DataFormat::Clean {
|
||||||
|
format!("{data}")
|
||||||
|
} else {
|
||||||
|
let end_text = end_text.unwrap_or("");
|
||||||
|
format!("{descriptor}: {data}{end_text}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::speed_unit::SpeedUnit;
|
||||||
|
use crate::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"));
|
||||||
|
}
|
||||||
|
}
|
33
src/current_weather_output_model.rs
Normal file
33
src/current_weather_output_model.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct CurrentWeatherOutputModel {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub latitude: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub longitude: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub city: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub is_day: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub temperature: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub windspeed: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub winddirection: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CurrentWeatherOutputModel {
|
||||||
|
pub fn new() -> CurrentWeatherOutputModel {
|
||||||
|
CurrentWeatherOutputModel {
|
||||||
|
latitude: None,
|
||||||
|
longitude: None,
|
||||||
|
city: None,
|
||||||
|
is_day: None,
|
||||||
|
temperature: None,
|
||||||
|
windspeed: None,
|
||||||
|
winddirection: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{speed_unit::SpeedUnit, temp_unit::TempUnit};
|
use crate::{data_format::DataFormat, speed_unit::SpeedUnit, temp_unit::TempUnit};
|
||||||
|
|
||||||
pub struct CurrentWeatherPrintParams {
|
pub struct CurrentWeatherPrintParams {
|
||||||
pub all: bool,
|
pub all: bool,
|
||||||
@ -10,7 +10,7 @@ pub struct CurrentWeatherPrintParams {
|
|||||||
pub winddirection: bool,
|
pub winddirection: bool,
|
||||||
pub include_coords: bool,
|
pub include_coords: bool,
|
||||||
pub include_city: bool,
|
pub include_city: bool,
|
||||||
pub clean: bool,
|
pub format: DataFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CurrentWeatherPrintParams {
|
impl CurrentWeatherPrintParams {
|
||||||
@ -24,7 +24,7 @@ impl CurrentWeatherPrintParams {
|
|||||||
winddirection: bool,
|
winddirection: bool,
|
||||||
include_coords: bool,
|
include_coords: bool,
|
||||||
include_city: bool,
|
include_city: bool,
|
||||||
clean: bool,
|
format: DataFormat,
|
||||||
) -> CurrentWeatherPrintParams {
|
) -> CurrentWeatherPrintParams {
|
||||||
CurrentWeatherPrintParams {
|
CurrentWeatherPrintParams {
|
||||||
all,
|
all,
|
||||||
@ -36,7 +36,7 @@ impl CurrentWeatherPrintParams {
|
|||||||
winddirection,
|
winddirection,
|
||||||
include_coords,
|
include_coords,
|
||||||
include_city,
|
include_city,
|
||||||
clean,
|
format,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,287 +0,0 @@
|
|||||||
use crate::speed_unit::speed_to_unit_string;
|
|
||||||
use crate::temp_unit::temp_to_unit_string;
|
|
||||||
use crate::{
|
|
||||||
coords::Coordinates, current_weather::CurrentWeather,
|
|
||||||
current_weather_print_params::CurrentWeatherPrintParams,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct CurrentWeatherPrinter {
|
|
||||||
current_weather: CurrentWeather,
|
|
||||||
params: CurrentWeatherPrintParams,
|
|
||||||
coords: Coordinates,
|
|
||||||
city: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CurrentWeatherPrinter {
|
|
||||||
pub fn new(
|
|
||||||
current_weather: CurrentWeather,
|
|
||||||
params: CurrentWeatherPrintParams,
|
|
||||||
coords: Coordinates,
|
|
||||||
city: Option<String>,
|
|
||||||
) -> CurrentWeatherPrinter {
|
|
||||||
CurrentWeatherPrinter {
|
|
||||||
current_weather,
|
|
||||||
params,
|
|
||||||
coords,
|
|
||||||
city,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extract_string(&self) -> String {
|
|
||||||
let mut string_vec: Vec<String> = Vec::new();
|
|
||||||
|
|
||||||
if !self.params.clean {
|
|
||||||
string_vec.push(self.create_header());
|
|
||||||
} else {
|
|
||||||
if self.params.all || self.params.include_coords {
|
|
||||||
string_vec.push(self.coords.latitude.to_string());
|
|
||||||
string_vec.push(self.coords.longitude.to_string());
|
|
||||||
}
|
|
||||||
if (self.params.all || self.params.include_city) && self.city.is_some() {
|
|
||||||
string_vec.push(self.city.clone().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.params.is_day || self.params.all {
|
|
||||||
string_vec.push(self.extract_day());
|
|
||||||
}
|
|
||||||
if self.params.temperature || self.params.all {
|
|
||||||
string_vec.push(self.extract_temperature());
|
|
||||||
}
|
|
||||||
if self.params.windspeed || self.params.all {
|
|
||||||
string_vec.push(self.extract_wind_speed());
|
|
||||||
}
|
|
||||||
if self.params.winddirection || self.params.all {
|
|
||||||
string_vec.push(self.extract_wind_direction());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.params.clean && (self.params.all || self.params.include_coords) {
|
|
||||||
string_vec.push(format!(
|
|
||||||
"{}, {}",
|
|
||||||
self.parse_simple_data(&self.coords.latitude.to_string(), "Latitude", None),
|
|
||||||
self.parse_simple_data(&self.coords.longitude.to_string(), "Longitude", None)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.params.clean {
|
|
||||||
let final_string = string_vec.join(",");
|
|
||||||
final_string
|
|
||||||
} else {
|
|
||||||
string_vec.push(self.create_footer());
|
|
||||||
string_vec.join("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_header(&self) -> String {
|
|
||||||
let mut title_header: Vec<String> = Vec::new();
|
|
||||||
title_header.push(String::from("=== Current weather"));
|
|
||||||
|
|
||||||
if (self.params.all || self.params.include_city) && self.city.is_some() {
|
|
||||||
title_header.push(String::from("for"));
|
|
||||||
title_header.push(self.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) -> String {
|
|
||||||
if self.current_weather.is_day == 1 {
|
|
||||||
self.parse_custom_data(&self.current_weather.is_day.to_string(), "Day")
|
|
||||||
} else {
|
|
||||||
self.parse_custom_data(&self.current_weather.is_day.to_string(), "Night")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_temperature(&self) -> String {
|
|
||||||
self.parse_simple_data(
|
|
||||||
&self.current_weather.temperature.to_string(),
|
|
||||||
"Temperature",
|
|
||||||
Some(temp_to_unit_string(&self.params.temperature_unit).as_str()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_wind_speed(&self) -> String {
|
|
||||||
self.parse_simple_data(
|
|
||||||
&self.current_weather.windspeed.to_string(),
|
|
||||||
"Wind speed",
|
|
||||||
Some(format!(" {}", speed_to_unit_string(&self.params.speed_unit)).as_str()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_wind_direction(&self) -> String {
|
|
||||||
self.parse_simple_data(
|
|
||||||
&self.current_weather.winddirection.to_string(),
|
|
||||||
"Wind direction",
|
|
||||||
Some("°"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_custom_data(&self, data: &str, custom: &str) -> String {
|
|
||||||
if self.params.clean {
|
|
||||||
format!("{data}")
|
|
||||||
} else {
|
|
||||||
format!("{custom}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_simple_data(&self, data: &str, descriptor: &str, end_text: Option<&str>) -> String {
|
|
||||||
if self.params.clean {
|
|
||||||
format!("{data}")
|
|
||||||
} else {
|
|
||||||
let end_text = end_text.unwrap_or("");
|
|
||||||
format!("{descriptor}: {data}{end_text}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::speed_unit::SpeedUnit;
|
|
||||||
use crate::temp_unit::TempUnit;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn clean_all_data() {
|
|
||||||
let current_weather = CurrentWeather {
|
|
||||||
is_day: 1,
|
|
||||||
temperature: 12.5,
|
|
||||||
windspeed: 7.0,
|
|
||||||
winddirection: 90.0,
|
|
||||||
};
|
|
||||||
let params = CurrentWeatherPrintParams::new(
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
TempUnit::Celsius,
|
|
||||||
false,
|
|
||||||
SpeedUnit::Kmh,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
let coords = Coordinates::new(5.0, -5.0);
|
|
||||||
let printer = CurrentWeatherPrinter::new(
|
|
||||||
current_weather,
|
|
||||||
params,
|
|
||||||
coords,
|
|
||||||
Some(String::from("TestCity")),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(printer.extract_string(), "5,-5,TestCity,1,12.5,7,90");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn clean_basic_data() {
|
|
||||||
let current_weather = CurrentWeather {
|
|
||||||
is_day: 1,
|
|
||||||
temperature: 15.5,
|
|
||||||
windspeed: 12.2,
|
|
||||||
winddirection: 150.0,
|
|
||||||
};
|
|
||||||
let params = CurrentWeatherPrintParams::new(
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
TempUnit::Celsius,
|
|
||||||
true,
|
|
||||||
SpeedUnit::Kmh,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
let coords = Coordinates::new(12.0, -55.0);
|
|
||||||
let printer = CurrentWeatherPrinter::new(
|
|
||||||
current_weather,
|
|
||||||
params,
|
|
||||||
coords,
|
|
||||||
Some(String::from("TestCity")),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(printer.extract_string(), "1,15.5,12.2");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn full_data() {
|
|
||||||
let current_weather = CurrentWeather {
|
|
||||||
is_day: 0,
|
|
||||||
temperature: 22.0,
|
|
||||||
windspeed: 15.5,
|
|
||||||
winddirection: 118.0,
|
|
||||||
};
|
|
||||||
let params = CurrentWeatherPrintParams::new(
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
TempUnit::Celsius,
|
|
||||||
false,
|
|
||||||
SpeedUnit::Kmh,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
let coords = Coordinates::new(5.0, -5.0);
|
|
||||||
let printer = CurrentWeatherPrinter::new(
|
|
||||||
current_weather,
|
|
||||||
params,
|
|
||||||
coords,
|
|
||||||
Some(String::from("TestCity")),
|
|
||||||
);
|
|
||||||
|
|
||||||
let output = printer.extract_string();
|
|
||||||
assert!(output.contains("Night"));
|
|
||||||
assert!(output.contains("Temperature: 22°C"));
|
|
||||||
assert!(output.contains("Wind speed: 15.5 km/h"));
|
|
||||||
assert!(output.contains("Wind direction: 118°"));
|
|
||||||
assert!(output.contains("Latitude: 5"));
|
|
||||||
assert!(output.contains("Longitude: -5"));
|
|
||||||
assert!(output.contains("TestCity"));
|
|
||||||
assert!(output.contains("Open-Meteo.com"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn full_basic_data() {
|
|
||||||
let current_weather = CurrentWeather {
|
|
||||||
is_day: 1,
|
|
||||||
temperature: 55.0,
|
|
||||||
windspeed: 11.5,
|
|
||||||
winddirection: 125.0,
|
|
||||||
};
|
|
||||||
let params = CurrentWeatherPrintParams::new(
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
TempUnit::Fahrenheit,
|
|
||||||
true,
|
|
||||||
SpeedUnit::Mph,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
let coords = Coordinates::new(12.15, 0.235);
|
|
||||||
let printer = CurrentWeatherPrinter::new(
|
|
||||||
current_weather,
|
|
||||||
params,
|
|
||||||
coords,
|
|
||||||
Some(String::from("NoCity")),
|
|
||||||
);
|
|
||||||
|
|
||||||
let output = printer.extract_string();
|
|
||||||
assert!(output.contains("Day"));
|
|
||||||
assert!(output.contains("Temperature: 55°F"));
|
|
||||||
assert!(output.contains("Wind speed: 11.5 mp/h"));
|
|
||||||
assert!(!output.contains("Wind direction: 125°"));
|
|
||||||
assert!(!output.contains("Latitude: 12.15"));
|
|
||||||
assert!(!output.contains("Longitude: 0.235"));
|
|
||||||
assert!(!output.contains("Nocity"));
|
|
||||||
assert!(output.contains("Open-Meteo.com"));
|
|
||||||
}
|
|
||||||
}
|
|
12
src/data_format.rs
Normal file
12
src/data_format.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use structopt::clap::arg_enum;
|
||||||
|
|
||||||
|
arg_enum! {
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
|
||||||
|
pub enum DataFormat {
|
||||||
|
Normal,
|
||||||
|
Clean,
|
||||||
|
JSON
|
||||||
|
}
|
||||||
|
}
|
31
src/main.rs
31
src/main.rs
@ -1,8 +1,12 @@
|
|||||||
mod cli;
|
mod cli;
|
||||||
mod coords;
|
mod coords;
|
||||||
mod current_weather;
|
mod current_weather;
|
||||||
|
mod current_weather_extractor;
|
||||||
|
mod current_weather_output;
|
||||||
|
mod current_weather_output_model;
|
||||||
|
|
||||||
mod current_weather_print_params;
|
mod current_weather_print_params;
|
||||||
mod current_weather_printer;
|
mod data_format;
|
||||||
mod ifconfig;
|
mod ifconfig;
|
||||||
mod ip_api;
|
mod ip_api;
|
||||||
mod open_meteo;
|
mod open_meteo;
|
||||||
@ -11,13 +15,14 @@ mod temp_unit;
|
|||||||
|
|
||||||
use billboard::{Alignment, Billboard};
|
use billboard::{Alignment, Billboard};
|
||||||
use current_weather_print_params::CurrentWeatherPrintParams;
|
use current_weather_print_params::CurrentWeatherPrintParams;
|
||||||
|
use data_format::DataFormat;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use cli::Arguments;
|
use cli::Arguments;
|
||||||
use coords::Coordinates;
|
use coords::Coordinates;
|
||||||
|
|
||||||
use current_weather_printer::CurrentWeatherPrinter;
|
use current_weather_extractor::CurrentWeatherExtractor;
|
||||||
use ifconfig::extract_public_ip;
|
use ifconfig::extract_public_ip;
|
||||||
use ip_api::extract_coords_and_city;
|
use ip_api::extract_coords_and_city;
|
||||||
use open_meteo::request_current_weather;
|
use open_meteo::request_current_weather;
|
||||||
@ -37,7 +42,9 @@ fn main() {
|
|||||||
winddirection,
|
winddirection,
|
||||||
include_coords,
|
include_coords,
|
||||||
include_city,
|
include_city,
|
||||||
|
mut format,
|
||||||
clean,
|
clean,
|
||||||
|
json,
|
||||||
} => {
|
} => {
|
||||||
if !all
|
if !all
|
||||||
&& !is_day
|
&& !is_day
|
||||||
@ -96,6 +103,12 @@ fn main() {
|
|||||||
exit(1);
|
exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if clean {
|
||||||
|
format = DataFormat::Clean;
|
||||||
|
} else if json {
|
||||||
|
format = DataFormat::JSON;
|
||||||
|
}
|
||||||
|
|
||||||
let print_params = CurrentWeatherPrintParams::new(
|
let print_params = CurrentWeatherPrintParams::new(
|
||||||
all,
|
all,
|
||||||
is_day,
|
is_day,
|
||||||
@ -106,20 +119,20 @@ fn main() {
|
|||||||
winddirection,
|
winddirection,
|
||||||
include_coords,
|
include_coords,
|
||||||
include_city,
|
include_city,
|
||||||
clean,
|
format,
|
||||||
);
|
);
|
||||||
|
|
||||||
let current_weather_printer =
|
let current_weather_printer =
|
||||||
CurrentWeatherPrinter::new(current_weather, print_params, coordinates, city);
|
CurrentWeatherExtractor::new(current_weather, print_params, coordinates, city);
|
||||||
let output = current_weather_printer.extract_string();
|
let output = current_weather_printer.extract_output();
|
||||||
if clean {
|
if DataFormat::Normal == current_weather_printer.params.format {
|
||||||
println!("{output}");
|
|
||||||
} else {
|
|
||||||
Billboard::builder()
|
Billboard::builder()
|
||||||
.text_alignment(Alignment::Left)
|
.text_alignment(Alignment::Left)
|
||||||
.box_alignment(Alignment::Left)
|
.box_alignment(Alignment::Left)
|
||||||
.build()
|
.build()
|
||||||
.eprint(output);
|
.eprint(output.to_string());
|
||||||
|
} else {
|
||||||
|
println!("{}", output.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use structopt::clap::arg_enum;
|
use structopt::clap::arg_enum;
|
||||||
|
|
||||||
arg_enum! {
|
arg_enum! {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||||
pub enum SpeedUnit {
|
pub enum SpeedUnit {
|
||||||
Kmh,
|
Kmh,
|
||||||
Ms,
|
Ms,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use structopt::clap::arg_enum;
|
use structopt::clap::arg_enum;
|
||||||
|
|
||||||
arg_enum! {
|
arg_enum! {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||||
pub enum TempUnit {
|
pub enum TempUnit {
|
||||||
Celsius,
|
Celsius,
|
||||||
Fahrenheit
|
Fahrenheit
|
||||||
|
Loading…
Reference in New Issue
Block a user