Adding automatic IP address, some refactor and better texts. Upgrading version.
This commit is contained in:
		
							
								
								
									
										144
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										144
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -34,6 +34,17 @@ version = "0.13.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "billboard"
 | 
			
		||||
version = "0.2.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "43a2281b8ed5442f678c1fbc0cc3a47ab897a136e2e3fc14ad8021259f0ded51"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "console",
 | 
			
		||||
 "term_size",
 | 
			
		||||
 "thiserror",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "bitflags"
 | 
			
		||||
version = "1.3.2"
 | 
			
		||||
@@ -73,6 +84,19 @@ dependencies = [
 | 
			
		||||
 "vec_map",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "console"
 | 
			
		||||
version = "0.15.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "encode_unicode",
 | 
			
		||||
 "lazy_static",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "unicode-width",
 | 
			
		||||
 "windows-sys",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crc32fast"
 | 
			
		||||
version = "1.3.2"
 | 
			
		||||
@@ -82,6 +106,12 @@ dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "encode_unicode"
 | 
			
		||||
version = "0.3.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "flate2"
 | 
			
		||||
version = "1.0.26"
 | 
			
		||||
@@ -182,8 +212,10 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "open-meteo-cli"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
version = "0.1.1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "billboard",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "structopt",
 | 
			
		||||
 "ureq",
 | 
			
		||||
@@ -285,6 +317,20 @@ name = "serde"
 | 
			
		||||
version = "1.0.163"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "serde_derive",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_derive"
 | 
			
		||||
version = "1.0.163"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn 2.0.16",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_json"
 | 
			
		||||
@@ -355,6 +401,16 @@ dependencies = [
 | 
			
		||||
 "unicode-ident",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "term_size"
 | 
			
		||||
version = "0.3.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc",
 | 
			
		||||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "textwrap"
 | 
			
		||||
version = "0.11.0"
 | 
			
		||||
@@ -364,6 +420,26 @@ dependencies = [
 | 
			
		||||
 "unicode-width",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "thiserror"
 | 
			
		||||
version = "1.0.40"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "thiserror-impl",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "thiserror-impl"
 | 
			
		||||
version = "1.0.40"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn 2.0.16",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "tinyvec"
 | 
			
		||||
version = "1.6.0"
 | 
			
		||||
@@ -563,3 +639,69 @@ name = "winapi-x86_64-pc-windows-gnu"
 | 
			
		||||
version = "0.4.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows-sys"
 | 
			
		||||
version = "0.45.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "windows-targets",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows-targets"
 | 
			
		||||
version = "0.42.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "windows_aarch64_gnullvm",
 | 
			
		||||
 "windows_aarch64_msvc",
 | 
			
		||||
 "windows_i686_gnu",
 | 
			
		||||
 "windows_i686_msvc",
 | 
			
		||||
 "windows_x86_64_gnu",
 | 
			
		||||
 "windows_x86_64_gnullvm",
 | 
			
		||||
 "windows_x86_64_msvc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_aarch64_gnullvm"
 | 
			
		||||
version = "0.42.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_aarch64_msvc"
 | 
			
		||||
version = "0.42.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_i686_gnu"
 | 
			
		||||
version = "0.42.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_i686_msvc"
 | 
			
		||||
version = "0.42.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_x86_64_gnu"
 | 
			
		||||
version = "0.42.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_x86_64_gnullvm"
 | 
			
		||||
version = "0.42.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_x86_64_msvc"
 | 
			
		||||
version = "0.42.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
 | 
			
		||||
 
 | 
			
		||||
@@ -3,15 +3,16 @@ name = "open-meteo-cli"
 | 
			
		||||
description = "CLI to extract meteorology data from Open Meteo"
 | 
			
		||||
license = "MIT"
 | 
			
		||||
authors = ["Midefos <midefos@gmail.com>"]
 | 
			
		||||
version = "0.1.1"
 | 
			
		||||
version = "0.1.2"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
repository = "https://git.midefos.com/midefos/open-meteo-cli/"
 | 
			
		||||
readme = "README.md"
 | 
			
		||||
keywords = ["weather", "open-meteo", "cli"]
 | 
			
		||||
categories = ["command-line-utilities"]
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
serde_json = "1.0.96"
 | 
			
		||||
structopt = "0.3.26"
 | 
			
		||||
serde = {version = "1.0.163", features = ["derive"]}
 | 
			
		||||
serde_json = "1.0.96"
 | 
			
		||||
ureq = {version = "2.6.2", features = ["json"]}
 | 
			
		||||
billboard = "0.2"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								README.md
									
									
									
									
									
								
							@@ -4,14 +4,12 @@ CLI to extract meteorology data from Open Meteo.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
The Open Meteo CLI allows you to retrieve weather data for specific coordinates.
 | 
			
		||||
 | 
			
		||||
### Current Weather
 | 
			
		||||
 | 
			
		||||
Get the current weather for a given coordinate.
 | 
			
		||||
Displays the current weather for your IP address automatically or for specific coordinates.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
open-meteo current-weather [OPTIONS] --latitude <LATITUDE> --longitude <LONGITUDE>
 | 
			
		||||
open-meteo-cli current-weather [OPTIONS] --latitude <LATITUDE> --longitude <LONGITUDE>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Options
 | 
			
		||||
@@ -19,30 +17,51 @@ open-meteo current-weather [OPTIONS] --latitude <LATITUDE> --longitude <LONGITUD
 | 
			
		||||
- `-L, --longitude <LONGITUDE>`: Longitude as a decimal number.
 | 
			
		||||
 | 
			
		||||
#### Flags
 | 
			
		||||
- `-a, --all`: Prints all available parameters.
 | 
			
		||||
- `-d, --is-day`: Prints if it is day or night.
 | 
			
		||||
- `-t, --temperature`: Prints the decimal temperature.
 | 
			
		||||
- `-w, --windspeed`: Prints the decimal wind speed.
 | 
			
		||||
- `-W, --winddirection`: Prints the wind direction angle.
 | 
			
		||||
- `-c, --clean`: Cleans the output and only shows the values separated by commas.
 | 
			
		||||
    - The order of values is as follows: is_day, temperature, windspeed, winddirection.
 | 
			
		||||
- `-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.
 | 
			
		||||
- `--coords`: Displays the latitude and the longitude.
 | 
			
		||||
- `--city`: Displays the city.
 | 
			
		||||
- `-c, --clean`: Cleans the output and only displays the values separated by commas.
 | 
			
		||||
    - The order of values is as follows: latitude, longitude, city, is_day, temperature, windspeed, winddirection.
 | 
			
		||||
 | 
			
		||||
#### Examples
 | 
			
		||||
- Standard usage: `open-meteo-cli current-weather -l 5 -L 5 -a`
 | 
			
		||||
 | 
			
		||||
- Standard usage: `open-meteo-cli current-weather -a`
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
=== Current weather for Latitude: 5, Longitude: 5 ===
 | 
			
		||||
Night
 | 
			
		||||
Temperature: 28.4°C
 | 
			
		||||
Wind speed: 17.0 km/h
 | 
			
		||||
Wind direction: 212.0°
 | 
			
		||||
=== Weather data by Open-Meteo.com ===
 | 
			
		||||
      ┌──────────────────────────────────────────────────┐
 | 
			
		||||
      │                                                  │
 | 
			
		||||
      │      === Current weather for Zaragoza ===        │
 | 
			
		||||
      │      Night                                       │
 | 
			
		||||
      │      Temperature: 18.8°C                         │
 | 
			
		||||
      │      Wind speed: 6.6 km/h                        │
 | 
			
		||||
      │      Wind direction: 22°                         │
 | 
			
		||||
      │      Latitude: 41.6405, Longitude: -0.8814       │
 | 
			
		||||
      │      === Weather data by Open-Meteo.com ===      │
 | 
			
		||||
      │                                                  │
 | 
			
		||||
      └──────────────────────────────────────────────────┘
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- Clean: `open-meteo-cli current-weather -l 12 -L=-3.4 -d -t -w -c`
 | 
			
		||||
- Using coordinates: `open-meteo-cli current-weather -l 12.2 -L=-0.38 -t -w`
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
0,26.5,15.5
 | 
			
		||||
      ┌──────────────────────────────────────────────────┐
 | 
			
		||||
      │                                                  │
 | 
			
		||||
      │      === Current weather ===                     │
 | 
			
		||||
      │      Temperature: 32.6°C                         │
 | 
			
		||||
      │      Wind speed: 12.5 km/h                       │
 | 
			
		||||
      │      === Weather data by Open-Meteo.com ===      │
 | 
			
		||||
      │                                                  │
 | 
			
		||||
      └──────────────────────────────────────────────────┘
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- Clean: `open-meteo-cli current-weather -d -t -w -c`
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
0,18.8,6.6
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Attribution
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								src/cli.rs
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								src/cli.rs
									
									
									
									
									
								
							@@ -6,29 +6,50 @@ use structopt::StructOpt;
 | 
			
		||||
    about = "CLI to extract meteorology data from Open Meteo"
 | 
			
		||||
)]
 | 
			
		||||
pub enum Arguments {
 | 
			
		||||
    #[structopt(about = "Gets the current weather for a coordinate")]
 | 
			
		||||
    #[structopt(
 | 
			
		||||
        about = "Displays the current weather for your IP address automatically or for specific coordinates"
 | 
			
		||||
    )]
 | 
			
		||||
    CurrentWeather {
 | 
			
		||||
        #[structopt(short = "l", long, help = "Latitude as a decimal number")]
 | 
			
		||||
        latitude: f32,
 | 
			
		||||
        #[structopt(short = "L", long, help = "Longitude as a decimal number")]
 | 
			
		||||
        longitude: f32,
 | 
			
		||||
        #[structopt(
 | 
			
		||||
            short = "l",
 | 
			
		||||
            long,
 | 
			
		||||
            requires("longitude"),
 | 
			
		||||
            help = "Latitude as a decimal number"
 | 
			
		||||
        )]
 | 
			
		||||
        latitude: Option<f64>,
 | 
			
		||||
        #[structopt(
 | 
			
		||||
            short = "L",
 | 
			
		||||
            long,
 | 
			
		||||
            requires("latitude"),
 | 
			
		||||
            help = "Longitude as a decimal number"
 | 
			
		||||
        )]
 | 
			
		||||
        longitude: Option<f64>,
 | 
			
		||||
 | 
			
		||||
        // Flags
 | 
			
		||||
        #[structopt(short = "a", long, help = "Prints all the parameters")]
 | 
			
		||||
        #[structopt(short = "a", long, help = "Displays all the information")]
 | 
			
		||||
        all: bool,
 | 
			
		||||
        #[structopt(short = "d", long, help = "Prints if it's day or night")]
 | 
			
		||||
        #[structopt(short = "d", long, help = "Displays if it's day or night")]
 | 
			
		||||
        is_day: bool,
 | 
			
		||||
        #[structopt(short = "t", long, help = "Prints the decimal temperature")]
 | 
			
		||||
        #[structopt(
 | 
			
		||||
            short = "t",
 | 
			
		||||
            long,
 | 
			
		||||
            help = "Displays the decimal temperature, in Celsius"
 | 
			
		||||
        )]
 | 
			
		||||
        temperature: bool,
 | 
			
		||||
        #[structopt(short = "w", long, help = "Prints the decimal wind speed")]
 | 
			
		||||
        #[structopt(short = "w", long, help = "Displays the decimal wind speed, in km/h")]
 | 
			
		||||
        windspeed: bool,
 | 
			
		||||
        #[structopt(short = "W", long, help = "Prints the wind direction angle")]
 | 
			
		||||
        #[structopt(short = "W", long, help = "Displays the wind direction, in degrees")]
 | 
			
		||||
        winddirection: bool,
 | 
			
		||||
        #[structopt(long = "coords", help = "Displays the latitude and the longitude")]
 | 
			
		||||
        include_coords: bool,
 | 
			
		||||
        #[structopt(long = "city", help = "Displays the city")]
 | 
			
		||||
        include_city: bool,
 | 
			
		||||
 | 
			
		||||
        #[structopt(
 | 
			
		||||
            short = "c",
 | 
			
		||||
            long,
 | 
			
		||||
            help = "Cleans the output and only shows the values separated by commas. 
 | 
			
		||||
- ORDER: is_day, temperature, windspeed, winddirection"
 | 
			
		||||
            help = "Cleans the output and only displays the values separated by commas. 
 | 
			
		||||
- ORDER: latitude, longitude, city, is_day, temperature, windspeed, winddirection"
 | 
			
		||||
        )]
 | 
			
		||||
        clean: bool,
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								src/coords.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/coords.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
pub struct Coordinates {
 | 
			
		||||
    pub latitude: f64,
 | 
			
		||||
    pub longitude: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Coordinates {
 | 
			
		||||
    pub fn new(latitude: f64, longitude: f64) -> Coordinates {
 | 
			
		||||
        Coordinates {
 | 
			
		||||
            latitude,
 | 
			
		||||
            longitude,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_correct_latitude(&self) -> bool {
 | 
			
		||||
        return self.latitude > -90.0 && self.latitude < 90.0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_correct_longitude(&self) -> bool {
 | 
			
		||||
        return self.longitude > -180.0 && self.longitude < 180.0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn valid_coords() {
 | 
			
		||||
        let coords = Coordinates {
 | 
			
		||||
            latitude: -20.0,
 | 
			
		||||
            longitude: 15.0,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        assert!(coords.is_correct_latitude());
 | 
			
		||||
        assert!(coords.is_correct_longitude());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn invalid_coords() {
 | 
			
		||||
        let coords = Coordinates {
 | 
			
		||||
            latitude: -95.0,
 | 
			
		||||
            longitude: 185.0,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        assert!(!coords.is_correct_latitude());
 | 
			
		||||
        assert!(!coords.is_correct_longitude());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
use serde_json::Value;
 | 
			
		||||
 | 
			
		||||
pub struct CurrentWeather {
 | 
			
		||||
    current_weather: serde_json::Value,
 | 
			
		||||
    clean: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CurrentWeather {
 | 
			
		||||
    pub fn new(current_weather: serde_json::Value, clean: bool) -> CurrentWeather {
 | 
			
		||||
        CurrentWeather {
 | 
			
		||||
            current_weather,
 | 
			
		||||
            clean,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn extract_simple_data(
 | 
			
		||||
        &self,
 | 
			
		||||
        key: &str,
 | 
			
		||||
        description: &str,
 | 
			
		||||
        end_text: Option<&str>,
 | 
			
		||||
    ) -> String {
 | 
			
		||||
        let data = self.extract_raw(key);
 | 
			
		||||
        self.parse_simple_data(data, description, end_text)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn extract_raw(&self, key: &str) -> &Value {
 | 
			
		||||
        &self.current_weather[key]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn parse_simple_data(&self, data: &Value, descriptor: &str, end_text: Option<&str>) -> String {
 | 
			
		||||
        if self.clean {
 | 
			
		||||
            format!("{data}")
 | 
			
		||||
        } else {
 | 
			
		||||
            let end_text = end_text.unwrap_or("");
 | 
			
		||||
            format!("{descriptor}: {data}{end_text}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn parse_custom_data(&self, data: &Value, custom: &str) -> String {
 | 
			
		||||
        if self.clean {
 | 
			
		||||
            format!("{data}")
 | 
			
		||||
        } else {
 | 
			
		||||
            format!("{custom}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_extract_data_clean() {
 | 
			
		||||
        let current_weather = serde_json::json!({
 | 
			
		||||
            "temperature": 25,
 | 
			
		||||
        });
 | 
			
		||||
        let weather = CurrentWeather::new(current_weather, true);
 | 
			
		||||
        assert_eq!(weather.extract_simple_data("temperature", "", None), "25");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_extract_data_not_clean() {
 | 
			
		||||
        let current_weather = serde_json::json!({
 | 
			
		||||
            "temperature": 25,
 | 
			
		||||
        });
 | 
			
		||||
        let weather = CurrentWeather::new(current_weather, false);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            weather.extract_simple_data("temperature", "Temperature", Some("°C")),
 | 
			
		||||
            "Temperature: 25°C"
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/current_weather.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/current_weather.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
pub struct CurrentWeather {
 | 
			
		||||
    pub is_day: i32,
 | 
			
		||||
    pub temperature: f64,
 | 
			
		||||
    pub windspeed: f64,
 | 
			
		||||
    pub winddirection: f64,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										100
									
								
								src/current_weather_printer.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/current_weather_printer.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
use crate::current_weather::CurrentWeather;
 | 
			
		||||
 | 
			
		||||
pub struct CurrentWeatherPrinter {
 | 
			
		||||
    current_weather: CurrentWeather,
 | 
			
		||||
    clean: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CurrentWeatherPrinter {
 | 
			
		||||
    pub fn new(current_weather: CurrentWeather, clean: bool) -> CurrentWeatherPrinter {
 | 
			
		||||
        CurrentWeatherPrinter {
 | 
			
		||||
            current_weather,
 | 
			
		||||
            clean,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub 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")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn extract_temperature(&self) -> String {
 | 
			
		||||
        self.parse_simple_data(
 | 
			
		||||
            &self.current_weather.temperature.to_string(),
 | 
			
		||||
            "Temperature",
 | 
			
		||||
            Some("°C"),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn extract_wind_speed(&self) -> String {
 | 
			
		||||
        self.parse_simple_data(
 | 
			
		||||
            &self.current_weather.windspeed.to_string(),
 | 
			
		||||
            "Wind speed",
 | 
			
		||||
            Some(" km/h"),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub 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.clean {
 | 
			
		||||
            format!("{data}")
 | 
			
		||||
        } else {
 | 
			
		||||
            format!("{custom}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn parse_simple_data(&self, data: &str, descriptor: &str, end_text: Option<&str>) -> String {
 | 
			
		||||
        if self.clean {
 | 
			
		||||
            format!("{data}")
 | 
			
		||||
        } else {
 | 
			
		||||
            let end_text = end_text.unwrap_or("");
 | 
			
		||||
            format!("{descriptor}: {data}{end_text}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn clean_data() {
 | 
			
		||||
        let current_weather = CurrentWeather {
 | 
			
		||||
            is_day: 1,
 | 
			
		||||
            temperature: 12.5,
 | 
			
		||||
            windspeed: 7.0,
 | 
			
		||||
            winddirection: 90.0,
 | 
			
		||||
        };
 | 
			
		||||
        let printer = CurrentWeatherPrinter::new(current_weather, true);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(printer.extract_day(), "1");
 | 
			
		||||
        assert_eq!(printer.extract_temperature(), "12.5");
 | 
			
		||||
        assert_eq!(printer.extract_wind_speed(), "7");
 | 
			
		||||
        assert_eq!(printer.extract_wind_direction(), "90");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn full_data() {
 | 
			
		||||
        let current_weather = CurrentWeather {
 | 
			
		||||
            is_day: 0,
 | 
			
		||||
            temperature: 22.0,
 | 
			
		||||
            windspeed: 15.5,
 | 
			
		||||
            winddirection: 118.0,
 | 
			
		||||
        };
 | 
			
		||||
        let printer = CurrentWeatherPrinter::new(current_weather, false);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(printer.extract_day(), "Night");
 | 
			
		||||
        assert_eq!(printer.extract_temperature(), "Temperature: 22°C");
 | 
			
		||||
        assert_eq!(printer.extract_wind_speed(), "Wind speed: 15.5 km/h");
 | 
			
		||||
        assert_eq!(printer.extract_wind_direction(), "Wind direction: 118°");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								src/ifconfig.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/ifconfig.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
pub fn extract_public_ip() -> Result<String, ureq::Error> {
 | 
			
		||||
    let body = ureq::get("https://ifconfig.me/ip").call()?.into_string()?;
 | 
			
		||||
    Ok(body)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								src/ip_api.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/ip_api.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
pub fn extract_coords_and_city(ip: &str) -> Result<serde_json::Value, ureq::Error> {
 | 
			
		||||
    let url = format!("http://ip-api.com/json/{}?fields=16592", ip);
 | 
			
		||||
    let body: serde_json::Value = ureq::get(&url).call()?.into_json()?;
 | 
			
		||||
    Ok(body)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										140
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,11 +1,23 @@
 | 
			
		||||
mod cli;
 | 
			
		||||
mod current_weater;
 | 
			
		||||
mod coords;
 | 
			
		||||
 | 
			
		||||
mod current_weather;
 | 
			
		||||
mod current_weather_printer;
 | 
			
		||||
mod ifconfig;
 | 
			
		||||
mod ip_api;
 | 
			
		||||
mod open_meteo;
 | 
			
		||||
 | 
			
		||||
use billboard::{Alignment, Billboard};
 | 
			
		||||
use std::process::exit;
 | 
			
		||||
use structopt::StructOpt;
 | 
			
		||||
 | 
			
		||||
use cli::Arguments;
 | 
			
		||||
use current_weater::CurrentWeather;
 | 
			
		||||
use coords::Coordinates;
 | 
			
		||||
 | 
			
		||||
use current_weather_printer::CurrentWeatherPrinter;
 | 
			
		||||
use ifconfig::extract_public_ip;
 | 
			
		||||
use ip_api::extract_coords_and_city;
 | 
			
		||||
use open_meteo::request_current_weather;
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let opts = Arguments::from_args();
 | 
			
		||||
@@ -18,60 +30,108 @@ fn main() {
 | 
			
		||||
            temperature,
 | 
			
		||||
            windspeed,
 | 
			
		||||
            winddirection,
 | 
			
		||||
            include_coords,
 | 
			
		||||
            include_city,
 | 
			
		||||
            clean,
 | 
			
		||||
        } => {
 | 
			
		||||
            if !all && !is_day && !temperature && !windspeed && !winddirection {
 | 
			
		||||
            if !all
 | 
			
		||||
                && !is_day
 | 
			
		||||
                && !temperature
 | 
			
		||||
                && !windspeed
 | 
			
		||||
                && !winddirection
 | 
			
		||||
                && !include_coords
 | 
			
		||||
                && !include_city
 | 
			
		||||
            {
 | 
			
		||||
                eprintln!("[ERROR] Provide at least one parameter to print, check --help");
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let result = extract_weather(latitude, longitude);
 | 
			
		||||
            let mut weather = result.unwrap_or_else(|_| {
 | 
			
		||||
                eprintln!("[ERROR] Requesting current_weather for Latitude: {latitude}, Longitude: {longitude}");
 | 
			
		||||
            let coordinates: Coordinates;
 | 
			
		||||
            let city: Option<String>;
 | 
			
		||||
 | 
			
		||||
            if latitude.is_some() && longitude.is_some() {
 | 
			
		||||
                let latitude = latitude.unwrap();
 | 
			
		||||
                let longitude = longitude.unwrap();
 | 
			
		||||
                coordinates = Coordinates::new(latitude, longitude);
 | 
			
		||||
 | 
			
		||||
                if !coordinates.is_correct_latitude() {
 | 
			
		||||
                    eprintln!("[ERROR] {} is not a valid latitude", coordinates.latitude);
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                } else if !coordinates.is_correct_longitude() {
 | 
			
		||||
                    eprintln!("[ERROR] {} is not a valid longitude", coordinates.longitude);
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                city = None;
 | 
			
		||||
            } else {
 | 
			
		||||
                let ip_result = extract_public_ip();
 | 
			
		||||
                let ip = ip_result.unwrap_or_else(|_| {
 | 
			
		||||
                    eprintln!("[ERROR] Could not extract public IP from ifconfig");
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let coords_result = extract_coords_and_city(&ip);
 | 
			
		||||
                let coords = coords_result.unwrap_or_else(|_| {
 | 
			
		||||
                    eprintln!("[ERROR] Could not extract IP coordinates from ip_api");
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let latitude = coords["lat"].as_f64().unwrap();
 | 
			
		||||
                let longitude = coords["lon"].as_f64().unwrap();
 | 
			
		||||
                coordinates = Coordinates::new(latitude, longitude);
 | 
			
		||||
                city = Some(coords["city"].as_str().unwrap().to_string());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let result = request_current_weather(&coordinates);
 | 
			
		||||
            let current_weather = result.unwrap_or_else(|_| {
 | 
			
		||||
                eprintln!(
 | 
			
		||||
                    "[ERROR] Requesting CurrentWeather for Lat: {}, Lon: {}",
 | 
			
		||||
                    coordinates.latitude, coordinates.longitude
 | 
			
		||||
                );
 | 
			
		||||
                exit(1);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            let mut string_vec: Vec<String> = Vec::new();
 | 
			
		||||
 | 
			
		||||
            if !clean {
 | 
			
		||||
                string_vec.push(format!(
 | 
			
		||||
                    "=== Current weather for Latitude: {latitude}, Longitude: {longitude} ==="
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
                let mut title_header: Vec<String> = Vec::new();
 | 
			
		||||
                title_header.push(String::from("=== Current weather"));
 | 
			
		||||
 | 
			
		||||
            let current_weather_data = weather["current_weather"].take();
 | 
			
		||||
            let current_weather = CurrentWeather::new(current_weather_data, clean);
 | 
			
		||||
                if (all || include_city) && city.is_some() {
 | 
			
		||||
                    title_header.push(String::from("for"));
 | 
			
		||||
                    title_header.push(city.unwrap());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            if is_day || all {
 | 
			
		||||
                let is_day = current_weather.extract_raw("is_day");
 | 
			
		||||
                if is_day == "1" {
 | 
			
		||||
                    string_vec.push(current_weather.parse_custom_data(is_day, "Day"));
 | 
			
		||||
                } else {
 | 
			
		||||
                    string_vec.push(current_weather.parse_custom_data(is_day, "Night"));
 | 
			
		||||
                title_header.push(String::from("==="));
 | 
			
		||||
                string_vec.push(title_header.join(" "));
 | 
			
		||||
            } else {
 | 
			
		||||
                if all || include_coords {
 | 
			
		||||
                    string_vec.push(coordinates.latitude.to_string());
 | 
			
		||||
                    string_vec.push(coordinates.longitude.to_string());
 | 
			
		||||
                }
 | 
			
		||||
                if (all || include_city) && city.is_some() {
 | 
			
		||||
                    string_vec.push(city.unwrap());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let current_weather_printer = CurrentWeatherPrinter::new(current_weather, clean);
 | 
			
		||||
            if is_day || all {
 | 
			
		||||
                string_vec.push(current_weather_printer.extract_day());
 | 
			
		||||
            }
 | 
			
		||||
            if temperature || all {
 | 
			
		||||
                string_vec.push(current_weather.extract_simple_data(
 | 
			
		||||
                    "temperature",
 | 
			
		||||
                    "Temperature",
 | 
			
		||||
                    Some("°C"),
 | 
			
		||||
                ));
 | 
			
		||||
                string_vec.push(current_weather_printer.extract_temperature());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if windspeed || all {
 | 
			
		||||
                string_vec.push(current_weather.extract_simple_data(
 | 
			
		||||
                    "windspeed",
 | 
			
		||||
                    "Wind speed",
 | 
			
		||||
                    Some(" km/h"),
 | 
			
		||||
                ));
 | 
			
		||||
                string_vec.push(current_weather_printer.extract_wind_speed());
 | 
			
		||||
            }
 | 
			
		||||
            if winddirection || all {
 | 
			
		||||
                string_vec.push(current_weather_printer.extract_wind_direction());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if winddirection || all {
 | 
			
		||||
                string_vec.push(current_weather.extract_simple_data(
 | 
			
		||||
                    "winddirection",
 | 
			
		||||
                    "Wind direction",
 | 
			
		||||
                    Some("°"),
 | 
			
		||||
            if !clean && (all || include_coords) {
 | 
			
		||||
                string_vec.push(format!(
 | 
			
		||||
                    "Latitude: {}, Longitude: {}",
 | 
			
		||||
                    coordinates.latitude, coordinates.longitude
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -81,14 +141,12 @@ fn main() {
 | 
			
		||||
            } else {
 | 
			
		||||
                string_vec.push(String::from("=== Weather data by Open-Meteo.com ==="));
 | 
			
		||||
                let final_string = string_vec.join("\n");
 | 
			
		||||
                println!("{final_string}");
 | 
			
		||||
                Billboard::builder()
 | 
			
		||||
                    .text_alignment(Alignment::Left)
 | 
			
		||||
                    .box_alignment(Alignment::Left)
 | 
			
		||||
                    .build()
 | 
			
		||||
                    .eprint(final_string);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn extract_weather(latitude: f32, longitude: f32) -> Result<serde_json::Value, ureq::Error> {
 | 
			
		||||
    let url = format!("https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}¤t_weather=true");
 | 
			
		||||
    let body: serde_json::Value = ureq::get(&url).call()?.into_json()?;
 | 
			
		||||
    Ok(body)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								src/open_meteo.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/open_meteo.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
use crate::{coords::Coordinates, current_weather::CurrentWeather};
 | 
			
		||||
 | 
			
		||||
pub fn request_current_weather(coords: &Coordinates) -> Result<CurrentWeather, ureq::Error> {
 | 
			
		||||
    let url = format!(
 | 
			
		||||
        "https://api.open-meteo.com/v1/forecast?latitude={}&longitude={}¤t_weather=true",
 | 
			
		||||
        coords.latitude, coords.longitude
 | 
			
		||||
    );
 | 
			
		||||
    let mut body: serde_json::Value = ureq::get(&url).call()?.into_json()?;
 | 
			
		||||
    let current_weather = body["current_weather"].take();
 | 
			
		||||
    let current_weather: CurrentWeather = serde_json::from_value(current_weather).unwrap();
 | 
			
		||||
    Ok(current_weather)
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user