Compare commits

...

26 Commits

Author SHA1 Message Date
58bae1772e upgrade, and refactor imports
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-08-21 04:23:25 +02:00
25e4348c1c cargo update/upgrade 2024-02-22 15:09:43 +01:00
f6bfebc8b4 cargo update
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-26 19:11:08 +02:00
3b9379d85c cargo fmt
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-19 20:01:36 +02:00
4e67706c04 clippy and update
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-19 20:01:27 +02:00
12378cb3c7 minor clena
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-30 18:24:17 +02:00
75292efa7c upgrading verison
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-30 18:15:49 +02:00
1affcb9d1a Correct stdout, updating lib and some clippy clean
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-30 18:13:42 +02:00
fd4affdb17 Ordering some files 2023-06-18 13:53:45 +02:00
0c3a3126f8 Adding cache for IpLocation
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-06-17 13:36:20 +02:00
a611f648fd Updating version
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-06-14 19:28:53 +02:00
ccddd8484c Adding JSON example in the README
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-06-14 19:24:31 +02:00
f677568513 Updating readme and updating cargo
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-06-14 19:19:05 +02:00
e481e5c44a Actualizar 'README.md'
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-06-13 18:48:32 +02:00
e5c5351abc Adding woodpecker.yml file
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-06-13 18:44:56 +02:00
a274ed7535 Cargo fmt 2023-06-13 18:41:14 +02:00
8a3e5783c2 Adding format to select between normal, clean and JSON. This needs some refactor 2023-06-13 18:38:32 +02:00
9978e1553b Updating cargo libs 2023-06-08 23:11:09 +02:00
cd04da0d91 Adding SpeedUnit and TempUnit 2023-06-08 23:10:21 +02:00
09df3cc6ed Typo naming 2023-05-26 20:30:17 +02:00
dbcf9437b6 Moving logic to current_weather_printer for better testing 2023-05-26 20:21:28 +02:00
0dd0f5ab49 Upgrading .lock file version 2023-05-23 01:17:46 +02:00
01ac2ca7b7 Adding automatic IP address, some refactor and better texts. Upgrading version. 2023-05-23 01:17:03 +02:00
3bd55576ce Upgrading version 2023-05-21 02:28:05 +02:00
b7d5df6f2a Clean CLI text 2023-05-21 02:27:37 +02:00
87cd8701e7 Better print values and updating README 2023-05-21 02:25:27 +02:00
21 changed files with 1372 additions and 275 deletions

5
.woodpecker.yml Normal file
View File

@ -0,0 +1,5 @@
pipeline:
test:
image: rust:latest
commands:
- cargo test

585
Cargo.lock generated
View File

@ -3,10 +3,10 @@
version = 3
[[package]]
name = "adler"
version = "1.0.2"
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "ansi_term"
@ -30,9 +30,20 @@ dependencies = [
[[package]]
name = "base64"
version = "0.13.1"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[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"
@ -41,16 +52,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.12.2"
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "cc"
version = "1.0.79"
version = "1.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
@ -66,7 +80,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"bitflags 1.3.2",
"strsim",
"textwrap",
"unicode-width",
@ -74,19 +88,59 @@ dependencies = [
]
[[package]]
name = "crc32fast"
version = "1.3.2"
name = "console"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys 0.52.0",
]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "flate2"
version = "1.0.26"
name = "directories"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
]
[[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.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666"
dependencies = [
"crc32fast",
"miniz_oxide",
@ -94,13 +148,24 @@ dependencies = [
[[package]]
name = "form_urlencoded"
version = "1.1.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "heck"
version = "0.3.3"
@ -121,9 +186,9 @@ dependencies = [
[[package]]
name = "idna"
version = "0.3.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
@ -131,69 +196,82 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.6"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "js-sys"
version = "0.3.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
dependencies = [
"wasm-bindgen",
]
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "lazy_static"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.144"
version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "log"
version = "0.4.17"
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"cfg-if",
"bitflags 2.6.0",
"libc",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
"adler",
"adler2",
]
[[package]]
name = "once_cell"
version = "1.17.1"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "open-meteo-cli"
version = "0.1.0"
version = "0.1.6"
dependencies = [
"billboard",
"directories",
"serde",
"serde_json",
"structopt",
"ureq",
]
[[package]]
name = "percent-encoding"
version = "2.2.0"
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "proc-macro-error"
@ -221,87 +299,129 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.58"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.27"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ring"
version = "0.16.20"
name = "redox_users"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]]
name = "ring"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.20.8"
version = "0.23.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
dependencies = [
"log",
"once_cell",
"ring",
"sct",
"webpki",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "ryu"
version = "1.0.13"
name = "rustls-pki-types"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "sct"
version = "0.7.0"
name = "rustls-webpki"
version = "0.102.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "serde"
version = "1.0.163"
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.208"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.208"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
]
[[package]]
name = "serde_json"
version = "1.0.96"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "spin"
version = "0.5.2"
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "strsim"
@ -333,6 +453,12 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "1.0.109"
@ -346,15 +472,25 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.16"
version = "2.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
dependencies = [
"proc-macro2",
"quote",
"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"
@ -365,10 +501,30 @@ dependencies = [
]
[[package]]
name = "tinyvec"
version = "1.6.0"
name = "thiserror"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
]
[[package]]
name = "tinyvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
dependencies = [
"tinyvec_macros",
]
@ -381,66 +537,66 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-ident"
version = "1.0.8"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.10"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]]
name = "untrusted"
version = "0.7.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.6.2"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d"
checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a"
dependencies = [
"base64",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
"url",
"webpki",
"webpki-roots",
]
[[package]]
name = "url"
version = "2.3.1"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
dependencies = [
"form_urlencoded",
"idna",
@ -455,91 +611,23 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasm-bindgen"
version = "0.2.86"
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.16",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.16",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
[[package]]
name = "web-sys"
version = "0.3.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [
"ring",
"untrusted",
]
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "webpki-roots"
version = "0.22.6"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd"
dependencies = [
"webpki",
"rustls-pki-types",
]
[[package]]
@ -563,3 +651,148 @@ 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.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"

View File

@ -3,15 +3,17 @@ name = "open-meteo-cli"
description = "CLI to extract meteorology data from Open Meteo"
license = "MIT"
authors = ["Midefos <midefos@gmail.com>"]
version = "0.1.0"
version = "0.1.6"
edition = "2021"
repository = "https://git.midefos.com/midefos/open-meteo-cli/"
readme = "README.md"
keywords = ["weather", "open-meteo", "cli"]
keywords = ["weather", "temperature", "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"
ureq = {version = "2.6.2", features = ["json"]}
serde = {version = "1.0.208", features = ["derive"]}
serde_json = "1.0.125"
ureq = {version = "2.10.1", features = ["json"]}
billboard = "0.2"
directories = "5.0.1"

View File

@ -1,30 +1,81 @@
# Open Meteo CLI
![Status](https://ci.midefos.com/api/badges/midefos/open-meteo-cli/status.svg)
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.
```plaintext
open-meteo 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.
- `--format <format>`: Switches data format between Normal, Clean or JSON. Default value is Normal.
- `-l, --latitude <LATITUDE>`: Latitude as a decimal number.
- `-L, --longitude <LONGITUDE>`: Longitude as a decimal number.
- `-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. Example output: `1,22.4,12.5,170.0`.
#### Flags
- `-a, --all`: Displays all the information.
- `-d, --is-day`: Displays if it is day or night.
- `-t, --temperature`: Displays the decimal temperature.
- `-w, --windspeed`: Displays the decimal wind speed.
- `-W, --winddirection`: Displays the wind direction, in degrees.
- `--coords`: Displays the latitude and the longitude.
- `--city`: Displays the city.
- `-c, --clean`: Displays the output separated by commas. Same as '--format clean'.
- The order of values is as follows: latitude, longitude, city, is_day, temperature, windspeed, winddirection.
- `-j, --json`: Displays the output as JSON. Same as '--format json'
#### Examples
- Standard usage: `open-meteo-cli current-weather -a`
```
┌──────────────────────────────────────────────────┐
│ │
│ === 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 === │
│ │
└──────────────────────────────────────────────────┘
```
- Using coordinates: `open-meteo-cli current-weather -l 12.2 -L=-0.38 -t -w`
```
┌──────────────────────────────────────────────────┐
│ │
│ === 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
```
- JSON: `open-meteo-cli current-weather -d -t -w -j`
```
{"is_day":0,"temperature":18.8,"windspeed":6.6}
```
## Attribution
The weather data used in this application is provided by [Open-Meteo.com](https://open-meteo.com/) under the [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) license. We would like to express our gratitude to Open-Meteo.com for their contribution to this project.
The weather data used in this application is provided by [Open-Meteo.com](https://open-meteo.com/) under the [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) license.
We would like to express our gratitude to Open-Meteo.com for their contribution to this project.

View File

@ -1,3 +1,6 @@
use crate::{
formats::data_format::DataFormat, formats::speed_unit::SpeedUnit, formats::temp_unit::TempUnit,
};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
@ -6,30 +9,63 @@ 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>,
#[structopt(short = "a", long, help = "Prints all the parameters")]
// Flags
#[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 is day or night")]
is_day: bool,
#[structopt(short = "t", long, help = "Prints the decimal temperature")]
#[structopt(short = "t", long, help = "Displays the decimal temperature")]
temperature: bool,
#[structopt(short = "w", long, help = "Prints the decimal wind speed")]
#[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(short = "W", long, help = "Prints the wind direction angle")]
#[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")]
include_coords: bool,
#[structopt(long = "city", help = "Displays the city")]
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(
short = "c",
long,
help = "Cleans the output and only shows the values separated by commas.
- ORDER: is_day, temperature, windspeed, winddirection
- EXAMPLE: 1,22.4,12.5,170.0"
help = "Displays the output separated by commas. Same as '--format clean'
- ORDER: latitude, longitude, city, is_day, temperature, windspeed, winddirection"
)]
clean: bool,
#[structopt(
short = "j",
long,
help = "Displays the output as JSON. Same as '--format json'"
)]
json: bool,
},
}

48
src/coords.rs Normal file
View 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 {
self.latitude > -90.0 && self.latitude < 90.0
}
pub fn is_correct_longitude(&self) -> bool {
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());
}
}

View File

@ -1,22 +0,0 @@
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_data(&self, data_name: &str, data_description: &str) -> String {
let data = &self.current_weather[data_name];
if self.clean {
format!("{data}")
} else {
format!("{data_description}: {data}")
}
}
}

View File

@ -0,0 +1,59 @@
use crate::{
coords::Coordinates, current_weather_output::CurrentWeatherOutput,
current_weather_print_params::CurrentWeatherPrintParams,
open_meteo::current_weather::CurrentWeather,
};
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);
}
output
}
}

View File

@ -0,0 +1,360 @@
use crate::{
current_weather_output_model::CurrentWeatherOutputModel,
formats::{
data_format::DataFormat,
speed_unit::{speed_to_unit_string, SpeedUnit},
temp_unit::{temp_to_unit_string, TempUnit},
},
};
use serde::{Deserialize, Serialize};
use std::fmt::Display;
#[derive(Serialize, Deserialize, Debug)]
pub struct CurrentWeatherOutput {
pub format: DataFormat,
pub temperature_unit: TempUnit,
pub speed_unit: SpeedUnit,
pub data: CurrentWeatherOutputModel,
}
impl Display for CurrentWeatherOutput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if DataFormat::JSON == self.format {
write!(f, "{}", 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 let Some(latitude) = self.data.latitude {
string_vec.push(latitude.to_string())
}
if let Some(longitude) = self.data.longitude {
string_vec.push(longitude.to_string())
}
if let Some(city) = &self.data.city {
string_vec.push(city.to_string())
}
}
let is_day = self.extract_day();
if let Some(is_day) = is_day {
string_vec.push(is_day);
}
let temperature = self.extract_temperature();
if let Some(temperature) = temperature {
string_vec.push(temperature);
}
let windspeed = self.extract_wind_speed();
if let Some(windspeed) = windspeed {
string_vec.push(windspeed);
}
let winddirection = self.extract_wind_direction();
if let Some(winddirection) = winddirection {
string_vec.push(winddirection);
}
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 {
write!(f, "{}", string_vec.join(","))
} else {
string_vec.push(self.create_footer());
write!(f, "{}", string_vec.join("\n"))
}
}
}
}
impl CurrentWeatherOutput {
pub fn new(
format: DataFormat,
temperature_unit: TempUnit,
speed_unit: SpeedUnit,
) -> CurrentWeatherOutput {
CurrentWeatherOutput {
format,
temperature_unit,
speed_unit,
data: CurrentWeatherOutputModel::new(),
}
}
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 {
data.to_string()
} else {
custom.to_string()
}
}
fn parse_simple_data(&self, data: &str, descriptor: &str, end_text: Option<&str>) -> String {
if self.format == DataFormat::Clean {
data.to_string()
} else {
let end_text = end_text.unwrap_or("");
format!("{descriptor}: {data}{end_text}")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::formats::speed_unit::SpeedUnit;
use crate::formats::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"));
}
}

View 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,
}
}
}

View File

@ -0,0 +1,16 @@
use crate::{
formats::data_format::DataFormat, formats::speed_unit::SpeedUnit, formats::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,
pub format: DataFormat,
}

View File

@ -0,0 +1,11 @@
use serde::{Deserialize, Serialize};
use structopt::clap::arg_enum;
arg_enum! {
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
pub enum DataFormat {
Normal,
Clean,
JSON
}
}

30
src/formats/speed_unit.rs Normal file
View File

@ -0,0 +1,30 @@
use serde::{Deserialize, Serialize};
use structopt::clap::arg_enum;
arg_enum! {
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
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/formats/temp_unit.rs Normal file
View File

@ -0,0 +1,24 @@
use serde::{Deserialize, Serialize};
use structopt::clap::arg_enum;
arg_enum! {
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
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(),
}
}

View 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)
}

9
src/ip_api/ip_api_api.rs Normal file
View File

@ -0,0 +1,9 @@
use crate::ip_api::ip_location::IpLocation;
pub fn extract_coords_and_city(ip: &str) -> Result<IpLocation, ureq::Error> {
let url = format!("http://ip-api.com/json/{}?fields=16592", ip);
let body: serde_json::Value = ureq::get(&url).call()?.into_json()?;
let current_weather: IpLocation = serde_json::from_value(body).unwrap();
Ok(current_weather)
}

View File

@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct IpLocation {
pub lat: f64,
pub lon: f64,
pub city: String,
}

View File

@ -0,0 +1,77 @@
use std::{
fs::{create_dir_all, File},
io::{Read, Write},
path::{Path, PathBuf},
};
use crate::ip_api::ip_location::IpLocation;
use directories::ProjectDirs;
pub fn save_to_cache(ip: &str, data: &IpLocation) {
let cache_dir = get_cache_dir();
if cache_dir.is_none() {
return;
}
let mut cache_dir = cache_dir.unwrap();
cache_dir.push(format!("{}.{}", ip, "json"));
let json = serde_json::to_string(data);
if json.is_err() {
return;
}
let file = File::create(&cache_dir);
if file.is_err() {
return;
}
let res = file.unwrap().write_all(json.unwrap().as_bytes());
if res.is_err() {
println!("[WARN] Error saving to cache IpLocation");
}
}
pub fn get_from_cache(ip: &str) -> Option<IpLocation> {
let mut cache_dir = get_cache_dir()?;
cache_dir.push(format!("{}.{}", ip, "json"));
if !is_in_cache(&cache_dir) {
return None;
}
let file = File::open(&cache_dir);
if file.is_err() {
return None;
}
let mut content = String::new();
let res = file.unwrap().read_to_string(&mut content);
if res.is_err() {
return None;
}
if let Ok(ip_location) = serde_json::from_str(&content) {
Some(ip_location)
} else {
None
}
}
fn is_in_cache(path: &Path) -> bool {
path.is_file()
}
fn get_cache_dir() -> Option<PathBuf> {
let dir = ProjectDirs::from("com", "midefos", "open-meteo-cli");
if let Some(project_dir) = &dir {
let cache_dir = project_dir.cache_dir();
if !cache_dir.exists() {
let res = create_dir_all(cache_dir);
if res.is_err() {
return None;
}
}
Some(cache_dir.to_owned())
} else {
None
}
}

View File

@ -1,12 +1,42 @@
mod cli;
mod current_weater;
mod coords;
mod current_weather_extractor;
mod current_weather_output;
mod current_weather_output_model;
mod current_weather_print_params;
mod ifconfig {
pub mod ifconfig_api;
}
mod formats {
pub mod data_format;
pub mod speed_unit;
pub mod temp_unit;
}
mod ip_api {
pub mod ip_api_api;
pub mod ip_location;
pub mod ip_location_cache;
}
mod open_meteo {
pub mod current_weather;
pub mod open_meteo_api;
}
use billboard::{Alignment, Billboard};
use cli::Arguments;
use coords::Coordinates;
use current_weather_extractor::CurrentWeatherExtractor;
use current_weather_print_params::CurrentWeatherPrintParams;
use formats::data_format::DataFormat;
use ifconfig::ifconfig_api::extract_public_ip;
use ip_api::{
ip_api_api::extract_coords_and_city,
ip_location_cache::{get_from_cache, save_to_cache},
};
use open_meteo::open_meteo_api::request_current_weather;
use std::process::exit;
use structopt::StructOpt;
use cli::Arguments;
use current_weater::CurrentWeather;
fn main() {
let opts = Arguments::from_args();
match opts {
@ -16,61 +46,112 @@ fn main() {
all,
is_day,
temperature,
temperature_unit,
windspeed,
speed_unit,
winddirection,
include_coords,
include_city,
mut format,
clean,
json,
} => {
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 ip_location;
let cache_ip_location = get_from_cache(&ip);
if let Some(cache_ip_location) = cache_ip_location {
ip_location = cache_ip_location;
} else {
let ip_location_res = extract_coords_and_city(&ip);
ip_location = ip_location_res.unwrap_or_else(|_| {
eprintln!("[ERROR] Could not extract IP location from ip_api");
exit(1);
});
save_to_cache(&ip, &ip_location);
}
let latitude = ip_location.lat;
let longitude = ip_location.lon;
coordinates = Coordinates::new(latitude, longitude);
city = Some(ip_location.city);
}
let result = request_current_weather(&coordinates, &temperature_unit, &speed_unit);
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 current_weather_data = weather["current_weather"].take();
let current_weather = CurrentWeather::new(current_weather_data, clean);
if is_day || all {
string_vec.push(current_weather.extract_data("is_day", "Is day"));
}
if temperature || all {
string_vec.push(current_weather.extract_data("temperature", "Temperature"));
}
if windspeed || all {
string_vec.push(current_weather.extract_data("windspeed", "Wind speed"));
}
if winddirection || all {
string_vec.push(current_weather.extract_data("winddirection", "Wind direction"));
}
if clean {
let final_string = string_vec.join(",");
println!("{final_string}");
format = DataFormat::Clean;
} else if json {
format = DataFormat::JSON;
}
let print_params = CurrentWeatherPrintParams {
all,
is_day,
temperature,
temperature_unit,
windspeed,
speed_unit,
winddirection,
include_coords,
include_city,
format,
};
let current_weather_printer =
CurrentWeatherExtractor::new(current_weather, print_params, coordinates, city);
let output = current_weather_printer.extract_output();
if DataFormat::Normal == current_weather_printer.params.format {
Billboard::builder()
.text_alignment(Alignment::Left)
.box_alignment(Alignment::Left)
.build()
.print(output);
} else {
let final_string = string_vec.join("\n");
println!("{final_string}");
println!("{}", output);
}
}
}
}
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}&current_weather=true");
let body: serde_json::Value = ureq::get(&url).call()?.into_json()?;
Ok(body)
}

View 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,
}

View File

@ -0,0 +1,23 @@
use crate::{
coords::Coordinates,
formats::{
speed_unit::{speed_to_string, SpeedUnit},
temp_unit::{temp_to_string, TempUnit},
},
open_meteo::current_weather::CurrentWeather,
};
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={}&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();
let current_weather: CurrentWeather = serde_json::from_value(current_weather).unwrap();
Ok(current_weather)
}