From cd04da0d91190382c67ed15651570bfc4836f4de Mon Sep 17 00:00:00 2001 From: Jorge Bolois Date: Thu, 8 Jun 2023 23:10:21 +0200 Subject: [PATCH] Adding SpeedUnit and TempUnit --- Cargo.toml | 2 +- README.md | 10 +-- src/cli.rs | 18 +++-- src/current_weather_print_params.rs | 8 +++ src/current_weather_printer.rs | 107 +++++++++++++++++++++------- src/main.rs | 14 ++-- src/open_meteo.rs | 17 +++-- src/speed_unit.rs | 30 ++++++++ src/temp_unit.rs | 24 +++++++ 9 files changed, 184 insertions(+), 46 deletions(-) create mode 100644 src/speed_unit.rs create mode 100644 src/temp_unit.rs diff --git a/Cargo.toml b/Cargo.toml index 2127ef2..027a80a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "open-meteo-cli" description = "CLI to extract meteorology data from Open Meteo" license = "MIT" authors = ["Midefos "] -version = "0.1.2" +version = "0.1.3" edition = "2021" repository = "https://git.midefos.com/midefos/open-meteo-cli/" readme = "README.md" diff --git a/README.md b/README.md index 12491f3..bdce042 100644 --- a/README.md +++ b/README.md @@ -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. ``` -open-meteo-cli current-weather [OPTIONS] --latitude --longitude +open-meteo-cli current-weather [FLAGS] [OPTIONS] ``` #### Options - `-l, --latitude `: Latitude as a decimal number. - `-L, --longitude `: Longitude as a decimal number. +- `--temperature-unit `: Switches between Celsius or Fahrenheit. Default value is Celsius. +- `--speed-unit `: Switches between km/h, m/s, mp/h or knots. Default value is Kmh. #### Flags - `-a, --all`: Displays all the information. - `-d, --is-day`: Displays if it's day or night. -- `-t, --temperature`: Displays the decimal temperature, in Celsius. -- `-w, --windspeed`: Displays the decimal wind speed, in km/h. -- `-W, --winddirection`: Displays the wind direction, in degrees. +- `-t, --temperature`: Displays the decimal temperature. +- `-w, --windspeed`: Displays the decimal wind speed. +- `-W, --winddirection`: Displays the wind direction. - `--coords`: Displays the latitude and the longitude. - `--city`: Displays the city. - `-c, --clean`: Cleans the output and only displays the values separated by commas. diff --git a/src/cli.rs b/src/cli.rs index 521c56a..feb8327 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,7 @@ use structopt::StructOpt; +use crate::{temp_unit::TempUnit, speed_unit::SpeedUnit}; + #[derive(Debug, StructOpt)] #[structopt( name = "open-meteo-cli", @@ -30,14 +32,18 @@ pub enum Arguments { all: bool, #[structopt(short = "d", long, help = "Displays if it's day or night")] is_day: bool, - #[structopt( - short = "t", - long, - help = "Displays the decimal temperature, in Celsius" - )] + #[structopt(short = "t", long, help = "Displays the decimal temperature")] 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, + #[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")] winddirection: bool, #[structopt(long = "coords", help = "Displays the latitude and the longitude")] diff --git a/src/current_weather_print_params.rs b/src/current_weather_print_params.rs index 70a19de..d1b665f 100644 --- a/src/current_weather_print_params.rs +++ b/src/current_weather_print_params.rs @@ -1,8 +1,12 @@ +use crate::{speed_unit::SpeedUnit, temp_unit::TempUnit}; + pub struct CurrentWeatherPrintParams { pub all: bool, pub is_day: bool, pub temperature: bool, + pub temperature_unit: TempUnit, pub windspeed: bool, + pub speed_unit: SpeedUnit, pub winddirection: bool, pub include_coords: bool, pub include_city: bool, @@ -14,7 +18,9 @@ impl CurrentWeatherPrintParams { all: bool, is_day: bool, temperature: bool, + temperature_unit: TempUnit, windspeed: bool, + speed_unit: SpeedUnit, winddirection: bool, include_coords: bool, include_city: bool, @@ -24,7 +30,9 @@ impl CurrentWeatherPrintParams { all, is_day, temperature, + temperature_unit, windspeed, + speed_unit, winddirection, include_coords, include_city, diff --git a/src/current_weather_printer.rs b/src/current_weather_printer.rs index 356aeaf..108405b 100644 --- a/src/current_weather_printer.rs +++ b/src/current_weather_printer.rs @@ -1,3 +1,5 @@ +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, @@ -29,16 +31,7 @@ impl CurrentWeatherPrinter { let mut string_vec: Vec = Vec::new(); if !self.params.clean { - let mut title_header: Vec = 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("===")); - string_vec.push(title_header.join(" ")); + string_vec.push(self.create_header()); } else { if self.params.all || self.params.include_coords { 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) { 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(","); final_string } else { - string_vec.push(String::from("=== Weather data by Open-Meteo.com ===")); + string_vec.push(self.create_footer()); string_vec.join("\n") } } + fn create_header(&self) -> String { + let mut title_header: Vec = 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") @@ -90,7 +101,7 @@ impl CurrentWeatherPrinter { self.parse_simple_data( &self.current_weather.temperature.to_string(), "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.current_weather.windspeed.to_string(), "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)] mod tests { use super::*; + use crate::speed_unit::SpeedUnit; + use crate::temp_unit::TempUnit; #[test] fn clean_all_data() { @@ -140,8 +153,18 @@ mod tests { windspeed: 7.0, winddirection: 90.0, }; - let params = - CurrentWeatherPrintParams::new(true, false, false, false, false, false, false, true); + 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, @@ -161,8 +184,18 @@ mod tests { windspeed: 12.2, winddirection: 150.0, }; - let params = - CurrentWeatherPrintParams::new(false, true, true, true, false, false, false, true); + 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, @@ -182,8 +215,18 @@ mod tests { windspeed: 15.5, winddirection: 118.0, }; - let params = - CurrentWeatherPrintParams::new(true, false, false, false, false, false, false, false); + 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, @@ -207,12 +250,22 @@ mod tests { fn full_basic_data() { let current_weather = CurrentWeather { is_day: 1, - temperature: 3.0, - windspeed: 22.5, + temperature: 55.0, + windspeed: 11.5, winddirection: 125.0, }; - let params = - CurrentWeatherPrintParams::new(false, true, true, true, false, false, false, false); + 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, @@ -223,8 +276,8 @@ mod tests { let output = printer.extract_string(); assert!(output.contains("Day")); - assert!(output.contains("Temperature: 3°C")); - assert!(output.contains("Wind speed: 22.5 km/h")); + 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")); diff --git a/src/main.rs b/src/main.rs index 648d959..6743c55 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ mod cli; mod coords; - -mod current_weather_print_params; mod current_weather; +mod current_weather_print_params; mod current_weather_printer; mod ifconfig; mod ip_api; mod open_meteo; +mod speed_unit; +mod temp_unit; use billboard::{Alignment, Billboard}; use current_weather_print_params::CurrentWeatherPrintParams; @@ -30,7 +31,9 @@ fn main() { all, is_day, temperature, + temperature_unit, windspeed, + speed_unit, winddirection, include_coords, include_city, @@ -84,7 +87,7 @@ fn main() { 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(|_| { eprintln!( "[ERROR] Requesting CurrentWeather for Lat: {}, Lon: {}", @@ -97,14 +100,17 @@ fn main() { all, is_day, temperature, + temperature_unit, windspeed, + speed_unit, winddirection, include_coords, include_city, 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(); if clean { println!("{output}"); diff --git a/src/open_meteo.rs b/src/open_meteo.rs index bfa5272..76354f3 100644 --- a/src/open_meteo.rs +++ b/src/open_meteo.rs @@ -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 { +pub fn request_current_weather( + coords: &Coordinates, + temp_unit: &TempUnit, + speed_unit: &SpeedUnit, +) -> Result { let url = format!( - "https://api.open-meteo.com/v1/forecast?latitude={}&longitude={}¤t_weather=true", - coords.latitude, coords.longitude + "https://api.open-meteo.com/v1/forecast?latitude={}&longitude={}&temperature_unit={}&windspeed_unit={}¤t_weather=true", + 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 current_weather = body["current_weather"].take(); diff --git a/src/speed_unit.rs b/src/speed_unit.rs new file mode 100644 index 0000000..56f4302 --- /dev/null +++ b/src/speed_unit.rs @@ -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(), + } +} diff --git a/src/temp_unit.rs b/src/temp_unit.rs new file mode 100644 index 0000000..96ffd56 --- /dev/null +++ b/src/temp_unit.rs @@ -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(), + } +}