Adding SpeedUnit and TempUnit

This commit is contained in:
Jorge Bolois 2023-06-08 23:10:21 +02:00
parent 09df3cc6ed
commit cd04da0d91
9 changed files with 184 additions and 46 deletions

View File

@ -3,7 +3,7 @@ name = "open-meteo-cli"
description = "CLI to extract meteorology data from Open Meteo" description = "CLI to extract meteorology data from Open Meteo"
license = "MIT" license = "MIT"
authors = ["Midefos <midefos@gmail.com>"] authors = ["Midefos <midefos@gmail.com>"]
version = "0.1.2" version = "0.1.3"
edition = "2021" edition = "2021"
repository = "https://git.midefos.com/midefos/open-meteo-cli/" repository = "https://git.midefos.com/midefos/open-meteo-cli/"
readme = "README.md" readme = "README.md"

View File

@ -9,19 +9,21 @@ CLI to extract meteorology data from Open Meteo.
Displays the current weather for your IP address automatically or for specific coordinates. Displays the current weather for your IP address automatically or for specific coordinates.
``` ```
open-meteo-cli current-weather [OPTIONS] --latitude <LATITUDE> --longitude <LONGITUDE> open-meteo-cli current-weather [FLAGS] [OPTIONS]
``` ```
#### Options #### Options
- `-l, --latitude <LATITUDE>`: Latitude as a decimal number. - `-l, --latitude <LATITUDE>`: Latitude as a decimal number.
- `-L, --longitude <LONGITUDE>`: Longitude as a decimal number. - `-L, --longitude <LONGITUDE>`: Longitude as a decimal number.
- `--temperature-unit <temperature-unit>`: Switches between Celsius or Fahrenheit. Default value is Celsius.
- `--speed-unit <speed-unit>`: Switches between km/h, m/s, mp/h or knots. Default value is Kmh.
#### Flags #### Flags
- `-a, --all`: Displays all the information. - `-a, --all`: Displays all the information.
- `-d, --is-day`: Displays if it's day or night. - `-d, --is-day`: Displays if it's day or night.
- `-t, --temperature`: Displays the decimal temperature, in Celsius. - `-t, --temperature`: Displays the decimal temperature.
- `-w, --windspeed`: Displays the decimal wind speed, in km/h. - `-w, --windspeed`: Displays the decimal wind speed.
- `-W, --winddirection`: Displays the wind direction, in degrees. - `-W, --winddirection`: Displays the wind direction.
- `--coords`: Displays the latitude and the longitude. - `--coords`: Displays the latitude and the longitude.
- `--city`: Displays the city. - `--city`: Displays the city.
- `-c, --clean`: Cleans the output and only displays the values separated by commas. - `-c, --clean`: Cleans the output and only displays the values separated by commas.

View File

@ -1,5 +1,7 @@
use structopt::StructOpt; use structopt::StructOpt;
use crate::{temp_unit::TempUnit, speed_unit::SpeedUnit};
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
#[structopt( #[structopt(
name = "open-meteo-cli", name = "open-meteo-cli",
@ -30,14 +32,18 @@ pub enum Arguments {
all: bool, all: bool,
#[structopt(short = "d", long, help = "Displays if it's day or night")] #[structopt(short = "d", long, help = "Displays if it's day or night")]
is_day: bool, is_day: bool,
#[structopt( #[structopt(short = "t", long, help = "Displays the decimal temperature")]
short = "t",
long,
help = "Displays the decimal temperature, in Celsius"
)]
temperature: bool, temperature: bool,
#[structopt(short = "w", long, help = "Displays the decimal wind speed, in km/h")] #[structopt(long,
possible_values = &TempUnit::variants(), default_value = "Celsius" , case_insensitive = true,
help = "Switches between Celsius or Fahrenheit")]
temperature_unit: TempUnit,
#[structopt(short = "w", long, help = "Displays the decimal wind speed")]
windspeed: bool, windspeed: bool,
#[structopt(long,
possible_values = &SpeedUnit::variants(), default_value = "Kmh" , case_insensitive = true,
help = "Switches between km/h, m/s, mp/h or knots")]
speed_unit: SpeedUnit,
#[structopt(short = "W", long, help = "Displays the wind direction, in degrees")] #[structopt(short = "W", long, help = "Displays the wind direction, in degrees")]
winddirection: bool, winddirection: bool,
#[structopt(long = "coords", help = "Displays the latitude and the longitude")] #[structopt(long = "coords", help = "Displays the latitude and the longitude")]

View File

@ -1,8 +1,12 @@
use crate::{speed_unit::SpeedUnit, temp_unit::TempUnit};
pub struct CurrentWeatherPrintParams { pub struct CurrentWeatherPrintParams {
pub all: bool, pub all: bool,
pub is_day: bool, pub is_day: bool,
pub temperature: bool, pub temperature: bool,
pub temperature_unit: TempUnit,
pub windspeed: bool, pub windspeed: bool,
pub speed_unit: SpeedUnit,
pub winddirection: bool, pub winddirection: bool,
pub include_coords: bool, pub include_coords: bool,
pub include_city: bool, pub include_city: bool,
@ -14,7 +18,9 @@ impl CurrentWeatherPrintParams {
all: bool, all: bool,
is_day: bool, is_day: bool,
temperature: bool, temperature: bool,
temperature_unit: TempUnit,
windspeed: bool, windspeed: bool,
speed_unit: SpeedUnit,
winddirection: bool, winddirection: bool,
include_coords: bool, include_coords: bool,
include_city: bool, include_city: bool,
@ -24,7 +30,9 @@ impl CurrentWeatherPrintParams {
all, all,
is_day, is_day,
temperature, temperature,
temperature_unit,
windspeed, windspeed,
speed_unit,
winddirection, winddirection,
include_coords, include_coords,
include_city, include_city,

View File

@ -1,3 +1,5 @@
use crate::speed_unit::speed_to_unit_string;
use crate::temp_unit::temp_to_unit_string;
use crate::{ use crate::{
coords::Coordinates, current_weather::CurrentWeather, coords::Coordinates, current_weather::CurrentWeather,
current_weather_print_params::CurrentWeatherPrintParams, current_weather_print_params::CurrentWeatherPrintParams,
@ -29,16 +31,7 @@ impl CurrentWeatherPrinter {
let mut string_vec: Vec<String> = Vec::new(); let mut string_vec: Vec<String> = Vec::new();
if !self.params.clean { if !self.params.clean {
let mut title_header: Vec<String> = Vec::new(); string_vec.push(self.create_header());
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("==="));
string_vec.push(title_header.join(" "));
} else { } else {
if self.params.all || self.params.include_coords { if self.params.all || self.params.include_coords {
string_vec.push(self.coords.latitude.to_string()); string_vec.push(self.coords.latitude.to_string());
@ -64,8 +57,9 @@ impl CurrentWeatherPrinter {
if !self.params.clean && (self.params.all || self.params.include_coords) { if !self.params.clean && (self.params.all || self.params.include_coords) {
string_vec.push(format!( string_vec.push(format!(
"Latitude: {}, Longitude: {}", "{}, {}",
self.coords.latitude, self.coords.longitude self.parse_simple_data(&self.coords.latitude.to_string(), "Latitude", None),
self.parse_simple_data(&self.coords.longitude.to_string(), "Longitude", None)
)); ));
} }
@ -73,11 +67,28 @@ impl CurrentWeatherPrinter {
let final_string = string_vec.join(","); let final_string = string_vec.join(",");
final_string final_string
} else { } else {
string_vec.push(String::from("=== Weather data by Open-Meteo.com ===")); string_vec.push(self.create_footer());
string_vec.join("\n") 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 { fn extract_day(&self) -> String {
if self.current_weather.is_day == 1 { if self.current_weather.is_day == 1 {
self.parse_custom_data(&self.current_weather.is_day.to_string(), "Day") self.parse_custom_data(&self.current_weather.is_day.to_string(), "Day")
@ -90,7 +101,7 @@ impl CurrentWeatherPrinter {
self.parse_simple_data( self.parse_simple_data(
&self.current_weather.temperature.to_string(), &self.current_weather.temperature.to_string(),
"Temperature", "Temperature",
Some("°C"), Some(temp_to_unit_string(&self.params.temperature_unit).as_str()),
) )
} }
@ -98,7 +109,7 @@ impl CurrentWeatherPrinter {
self.parse_simple_data( self.parse_simple_data(
&self.current_weather.windspeed.to_string(), &self.current_weather.windspeed.to_string(),
"Wind speed", "Wind speed",
Some(" km/h"), Some(format!(" {}", speed_to_unit_string(&self.params.speed_unit)).as_str()),
) )
} }
@ -131,6 +142,8 @@ impl CurrentWeatherPrinter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::speed_unit::SpeedUnit;
use crate::temp_unit::TempUnit;
#[test] #[test]
fn clean_all_data() { fn clean_all_data() {
@ -140,8 +153,18 @@ mod tests {
windspeed: 7.0, windspeed: 7.0,
winddirection: 90.0, winddirection: 90.0,
}; };
let params = let params = CurrentWeatherPrintParams::new(
CurrentWeatherPrintParams::new(true, false, false, false, false, false, false, true); true,
false,
false,
TempUnit::Celsius,
false,
SpeedUnit::Kmh,
false,
false,
false,
true,
);
let coords = Coordinates::new(5.0, -5.0); let coords = Coordinates::new(5.0, -5.0);
let printer = CurrentWeatherPrinter::new( let printer = CurrentWeatherPrinter::new(
current_weather, current_weather,
@ -161,8 +184,18 @@ mod tests {
windspeed: 12.2, windspeed: 12.2,
winddirection: 150.0, winddirection: 150.0,
}; };
let params = let params = CurrentWeatherPrintParams::new(
CurrentWeatherPrintParams::new(false, true, true, true, false, false, false, true); false,
true,
true,
TempUnit::Celsius,
true,
SpeedUnit::Kmh,
false,
false,
false,
true,
);
let coords = Coordinates::new(12.0, -55.0); let coords = Coordinates::new(12.0, -55.0);
let printer = CurrentWeatherPrinter::new( let printer = CurrentWeatherPrinter::new(
current_weather, current_weather,
@ -182,8 +215,18 @@ mod tests {
windspeed: 15.5, windspeed: 15.5,
winddirection: 118.0, winddirection: 118.0,
}; };
let params = let params = CurrentWeatherPrintParams::new(
CurrentWeatherPrintParams::new(true, false, false, false, false, false, false, false); true,
false,
false,
TempUnit::Celsius,
false,
SpeedUnit::Kmh,
false,
false,
false,
false,
);
let coords = Coordinates::new(5.0, -5.0); let coords = Coordinates::new(5.0, -5.0);
let printer = CurrentWeatherPrinter::new( let printer = CurrentWeatherPrinter::new(
current_weather, current_weather,
@ -207,12 +250,22 @@ mod tests {
fn full_basic_data() { fn full_basic_data() {
let current_weather = CurrentWeather { let current_weather = CurrentWeather {
is_day: 1, is_day: 1,
temperature: 3.0, temperature: 55.0,
windspeed: 22.5, windspeed: 11.5,
winddirection: 125.0, winddirection: 125.0,
}; };
let params = let params = CurrentWeatherPrintParams::new(
CurrentWeatherPrintParams::new(false, true, true, true, false, false, false, false); false,
true,
true,
TempUnit::Fahrenheit,
true,
SpeedUnit::Mph,
false,
false,
false,
false,
);
let coords = Coordinates::new(12.15, 0.235); let coords = Coordinates::new(12.15, 0.235);
let printer = CurrentWeatherPrinter::new( let printer = CurrentWeatherPrinter::new(
current_weather, current_weather,
@ -223,8 +276,8 @@ mod tests {
let output = printer.extract_string(); let output = printer.extract_string();
assert!(output.contains("Day")); assert!(output.contains("Day"));
assert!(output.contains("Temperature: 3°C")); assert!(output.contains("Temperature: 55°F"));
assert!(output.contains("Wind speed: 22.5 km/h")); assert!(output.contains("Wind speed: 11.5 mp/h"));
assert!(!output.contains("Wind direction: 125°")); assert!(!output.contains("Wind direction: 125°"));
assert!(!output.contains("Latitude: 12.15")); assert!(!output.contains("Latitude: 12.15"));
assert!(!output.contains("Longitude: 0.235")); assert!(!output.contains("Longitude: 0.235"));

View File

@ -1,12 +1,13 @@
mod cli; mod cli;
mod coords; mod coords;
mod current_weather_print_params;
mod current_weather; mod current_weather;
mod current_weather_print_params;
mod current_weather_printer; mod current_weather_printer;
mod ifconfig; mod ifconfig;
mod ip_api; mod ip_api;
mod open_meteo; mod open_meteo;
mod speed_unit;
mod temp_unit;
use billboard::{Alignment, Billboard}; use billboard::{Alignment, Billboard};
use current_weather_print_params::CurrentWeatherPrintParams; use current_weather_print_params::CurrentWeatherPrintParams;
@ -30,7 +31,9 @@ fn main() {
all, all,
is_day, is_day,
temperature, temperature,
temperature_unit,
windspeed, windspeed,
speed_unit,
winddirection, winddirection,
include_coords, include_coords,
include_city, include_city,
@ -84,7 +87,7 @@ fn main() {
city = Some(coords["city"].as_str().unwrap().to_string()); city = Some(coords["city"].as_str().unwrap().to_string());
} }
let result = request_current_weather(&coordinates); let result = request_current_weather(&coordinates, &temperature_unit, &speed_unit);
let current_weather = result.unwrap_or_else(|_| { let current_weather = result.unwrap_or_else(|_| {
eprintln!( eprintln!(
"[ERROR] Requesting CurrentWeather for Lat: {}, Lon: {}", "[ERROR] Requesting CurrentWeather for Lat: {}, Lon: {}",
@ -97,14 +100,17 @@ fn main() {
all, all,
is_day, is_day,
temperature, temperature,
temperature_unit,
windspeed, windspeed,
speed_unit,
winddirection, winddirection,
include_coords, include_coords,
include_city, include_city,
clean, clean,
); );
let current_weather_printer = CurrentWeatherPrinter::new(current_weather, print_params, coordinates, city); let current_weather_printer =
CurrentWeatherPrinter::new(current_weather, print_params, coordinates, city);
let output = current_weather_printer.extract_string(); let output = current_weather_printer.extract_string();
if clean { if clean {
println!("{output}"); println!("{output}");

View File

@ -1,9 +1,18 @@
use crate::{coords::Coordinates, current_weather::CurrentWeather}; use crate::{
coords::Coordinates,
current_weather::CurrentWeather,
speed_unit::{speed_to_string, SpeedUnit},
temp_unit::{temp_to_string, TempUnit},
};
pub fn request_current_weather(coords: &Coordinates) -> Result<CurrentWeather, ureq::Error> { pub fn request_current_weather(
coords: &Coordinates,
temp_unit: &TempUnit,
speed_unit: &SpeedUnit,
) -> Result<CurrentWeather, ureq::Error> {
let url = format!( let url = format!(
"https://api.open-meteo.com/v1/forecast?latitude={}&longitude={}&current_weather=true", "https://api.open-meteo.com/v1/forecast?latitude={}&longitude={}&temperature_unit={}&windspeed_unit={}&current_weather=true",
coords.latitude, coords.longitude coords.latitude, coords.longitude, temp_to_string(temp_unit), speed_to_string(speed_unit)
); );
let mut body: serde_json::Value = ureq::get(&url).call()?.into_json()?; let mut body: serde_json::Value = ureq::get(&url).call()?.into_json()?;
let current_weather = body["current_weather"].take(); let current_weather = body["current_weather"].take();

30
src/speed_unit.rs Normal file
View File

@ -0,0 +1,30 @@
use structopt::clap::arg_enum;
arg_enum! {
#[derive(Debug)]
pub enum SpeedUnit {
Kmh,
Ms,
Mph,
Knots
}
}
pub fn speed_to_unit_string(speed_unit: &SpeedUnit) -> String {
match speed_unit {
SpeedUnit::Kmh => "km/h".to_string(),
SpeedUnit::Ms => "m/s".to_string(),
SpeedUnit::Mph => "mp/h".to_string(),
SpeedUnit::Knots => "knots".to_string(),
}
}
pub fn speed_to_string(speed_unit: &SpeedUnit) -> String {
match speed_unit {
SpeedUnit::Kmh => "kmh".to_string(),
SpeedUnit::Ms => "ms".to_string(),
SpeedUnit::Mph => "mph".to_string(),
SpeedUnit::Knots => "kn".to_string(),
}
}

24
src/temp_unit.rs Normal file
View File

@ -0,0 +1,24 @@
use structopt::clap::arg_enum;
arg_enum! {
#[derive(Debug)]
pub enum TempUnit {
Celsius,
Fahrenheit
}
}
pub fn temp_to_unit_string(temp_unit: &TempUnit) -> String {
match temp_unit {
TempUnit::Celsius => "°C".to_string(),
TempUnit::Fahrenheit => "°F".to_string(),
}
}
pub fn temp_to_string(temp_unit: &TempUnit) -> String {
match temp_unit {
TempUnit::Celsius => "celsius".to_string(),
TempUnit::Fahrenheit => "fahrenheit".to_string(),
}
}