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"
license = "MIT"
authors = ["Midefos <midefos@gmail.com>"]
version = "0.1.2"
version = "0.1.3"
edition = "2021"
repository = "https://git.midefos.com/midefos/open-meteo-cli/"
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.
```
open-meteo-cli current-weather [OPTIONS] --latitude <LATITUDE> --longitude <LONGITUDE>
open-meteo-cli current-weather [FLAGS] [OPTIONS]
```
#### Options
- `-l, --latitude <LATITUDE>`: Latitude 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
- `-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.

View File

@ -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")]

View File

@ -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,

View File

@ -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<String> = Vec::new();
if !self.params.clean {
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("==="));
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<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")
@ -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"));

View File

@ -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}");

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!(
"https://api.open-meteo.com/v1/forecast?latitude={}&longitude={}&current_weather=true",
coords.latitude, coords.longitude
"https://api.open-meteo.com/v1/forecast?latitude={}&longitude={}&temperature_unit={}&windspeed_unit={}&current_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();

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(),
}
}