Compare commits
26 Commits
807e8071da
...
main
Author | SHA1 | Date | |
---|---|---|---|
58bae1772e | |||
25e4348c1c | |||
f6bfebc8b4 | |||
3b9379d85c | |||
4e67706c04 | |||
12378cb3c7 | |||
75292efa7c | |||
1affcb9d1a | |||
fd4affdb17 | |||
0c3a3126f8 | |||
a611f648fd | |||
ccddd8484c | |||
f677568513 | |||
e481e5c44a | |||
e5c5351abc | |||
a274ed7535 | |||
8a3e5783c2 | |||
9978e1553b | |||
cd04da0d91 | |||
09df3cc6ed | |||
dbcf9437b6 | |||
0dd0f5ab49 | |||
01ac2ca7b7 | |||
3bd55576ce | |||
b7d5df6f2a | |||
87cd8701e7 |
5
.woodpecker.yml
Normal file
5
.woodpecker.yml
Normal file
@ -0,0 +1,5 @@
|
||||
pipeline:
|
||||
test:
|
||||
image: rust:latest
|
||||
commands:
|
||||
- cargo test
|
585
Cargo.lock
generated
585
Cargo.lock
generated
@ -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"
|
||||
|
12
Cargo.toml
12
Cargo.toml
@ -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"
|
||||
|
79
README.md
79
README.md
@ -1,30 +1,81 @@
|
||||
# Open Meteo CLI
|
||||

|
||||
|
||||
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.
|
||||
|
62
src/cli.rs
62
src/cli.rs
@ -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
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 {
|
||||
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());
|
||||
}
|
||||
}
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
59
src/current_weather_extractor.rs
Normal file
59
src/current_weather_extractor.rs
Normal 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
|
||||
}
|
||||
}
|
360
src/current_weather_output.rs
Normal file
360
src/current_weather_output.rs
Normal 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"));
|
||||
}
|
||||
}
|
33
src/current_weather_output_model.rs
Normal file
33
src/current_weather_output_model.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
16
src/current_weather_print_params.rs
Normal file
16
src/current_weather_print_params.rs
Normal 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,
|
||||
}
|
11
src/formats/data_format.rs
Normal file
11
src/formats/data_format.rs
Normal 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
30
src/formats/speed_unit.rs
Normal 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
24
src/formats/temp_unit.rs
Normal 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(),
|
||||
}
|
||||
}
|
4
src/ifconfig/ifconfig_api.rs
Normal file
4
src/ifconfig/ifconfig_api.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)
|
||||
}
|
9
src/ip_api/ip_api_api.rs
Normal file
9
src/ip_api/ip_api_api.rs
Normal 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)
|
||||
}
|
8
src/ip_api/ip_location.rs
Normal file
8
src/ip_api/ip_location.rs
Normal 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,
|
||||
}
|
77
src/ip_api/ip_location_cache.rs
Normal file
77
src/ip_api/ip_location_cache.rs
Normal 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
|
||||
}
|
||||
}
|
171
src/main.rs
171
src/main.rs
@ -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}¤t_weather=true");
|
||||
let body: serde_json::Value = ureq::get(&url).call()?.into_json()?;
|
||||
Ok(body)
|
||||
}
|
||||
|
9
src/open_meteo/current_weather.rs
Normal file
9
src/open_meteo/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,
|
||||
}
|
23
src/open_meteo/open_meteo_api.rs
Normal file
23
src/open_meteo/open_meteo_api.rs
Normal 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={}¤t_weather=true",
|
||||
coords.latitude, coords.longitude, temp_to_string(temp_unit), speed_to_string(speed_unit)
|
||||
);
|
||||
let mut body: serde_json::Value = ureq::get(&url).call()?.into_json()?;
|
||||
let current_weather = body["current_weather"].take();
|
||||
let current_weather: CurrentWeather = serde_json::from_value(current_weather).unwrap();
|
||||
Ok(current_weather)
|
||||
}
|
Reference in New Issue
Block a user