gping-1.16.0/.cargo_vcs_info.json0000644000000001430000000000100122230ustar { "git": { "sha1": "7547db0d2d8c22cf15066d51fcd41dc57cb5b046" }, "path_in_vcs": "gping" }gping-1.16.0/Cargo.lock0000644000000763010000000000100102070ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ahash" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "once_cell", "version_check", "zerocopy", ] [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anstream" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys", ] [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "cassowary" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", "windows-targets", ] [[package]] name = "clap" version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "const_fn" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" [[package]] name = "const_format" version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "core-foundation-sys" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "crossterm" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ "bitflags 2.4.1", "crossterm_winapi", "libc", "mio", "parking_lot", "signal-hook", "signal-hook-mio", "winapi", ] [[package]] name = "crossterm_winapi" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ "winapi", ] [[package]] name = "deranged" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ "powerfmt", ] [[package]] name = "dns-lookup" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" dependencies = [ "cfg-if", "libc", "socket2", "windows-sys", ] [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "getrandom" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "git2" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd" dependencies = [ "bitflags 2.4.1", "libc", "libgit2-sys", "log", "url", ] [[package]] name = "gping" version = "1.16.0" dependencies = [ "anyhow", "chrono", "clap", "const_format", "crossterm", "dns-lookup", "itertools 0.12.0", "pinger", "ratatui", "shadow-rs", ] [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", ] [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "iana-time-zone" version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "idna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "indoc" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" [[package]] name = "is_debug" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06d198e9919d9822d5f7083ba8530e04de87841eaf21ead9af8f2304efd57c89" [[package]] name = "itertools" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] [[package]] name = "itertools" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy-regex" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c" dependencies = [ "lazy-regex-proc_macros", "once_cell", "regex", ] [[package]] name = "lazy-regex-proc_macros" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b" dependencies = [ "proc-macro2", "quote", "regex", "syn", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libgit2-sys" version = "0.16.1+1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c" dependencies = [ "cc", "libc", "libz-sys", "pkg-config", ] [[package]] name = "libz-sys" version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "lock_api" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" dependencies = [ "hashbrown", ] [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mio" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "log", "wasi", "windows-sys", ] [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "num_threads" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets", ] [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pinger" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "350b97bd37731521ca085352c905431960d818970f0b0aa082cc3cbf57a0444e" dependencies = [ "anyhow", "dns-lookup", "lazy-regex", "rand", "thiserror", "winping", ] [[package]] name = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "ratatui" version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ebc917cfb527a566c37ecb94c7e3fd098353516fb4eb6bea17015ade0182425" dependencies = [ "bitflags 2.4.1", "cassowary", "crossterm", "indoc", "itertools 0.11.0", "lru", "paste", "strum", "unicode-segmentation", "unicode-width", ] [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustversion" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "shadow-rs" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9198caff1c94f1a5df6664bddbc379896b51b98a55b0b3fedcb23078fe00c77" dependencies = [ "const_format", "git2", "is_debug", "time", "tzdb", ] [[package]] name = "signal-hook" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-mio" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", "mio", "signal-hook", ] [[package]] name = "signal-hook-registry" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "socket2" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys", ] [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", "syn", ] [[package]] name = "syn" version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "time" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", "libc", "num_threads", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tz-rs" version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4" dependencies = [ "const_fn", ] [[package]] name = "tzdb" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec758958f2fb5069cd7fae385be95cc8eceb8cdfd270c7d14de6034f0108d99e" dependencies = [ "iana-time-zone", "tz-rs", ] [[package]] name = "unicode-bidi" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unicode-xid" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] 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 = "winapi_forked_icmpapi" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42aecb895d6340af9ccc8dab9aeabfeab6d5d7266c5fd172c8be7e07db71c1e3" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "windows-core" version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[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_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[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_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winping" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79ed0e3a789beb896b3de9fb7e93c76340f6f4adfab7770d6222b4b8625ef0aa" dependencies = [ "lazy_static", "static_assertions", "winapi_forked_icmpapi", ] [[package]] name = "zerocopy" version = "0.7.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" dependencies = [ "proc-macro2", "quote", "syn", ] gping-1.16.0/Cargo.toml0000644000000024060000000000100102250ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "gping" version = "1.16.0" authors = ["Tom Forbes "] build = "build.rs" description = "Ping, but with a graph." readme = "readme.md" license = "MIT" repository = "https://github.com/orf/gping" [dependencies.anyhow] version = "1.0.75" [dependencies.chrono] version = "0.4.31" [dependencies.clap] version = "4.4.10" features = ["derive"] [dependencies.const_format] version = "0.2.32" [dependencies.crossterm] version = "0.27.0" [dependencies.dns-lookup] version = "2.0.0" [dependencies.itertools] version = "0.12.0" [dependencies.pinger] version = "^1.1.0" [dependencies.shadow-rs] version = "0.24.1" [dependencies.tui] version = "0.24.0" features = ["crossterm"] default_features = false package = "ratatui" [build-dependencies.shadow-rs] version = "0.24.1" gping-1.16.0/Cargo.toml.orig000064400000000000000000000012201046102023000136770ustar 00000000000000[package] name = "gping" version = "1.16.0" authors = ["Tom Forbes "] edition = "2018" repository = "https://github.com/orf/gping" license = "MIT" description = "Ping, but with a graph." build = "build.rs" readme = "../readme.md" [dependencies] pinger = { version = "^1.1.0", path = "../pinger" } tui = { package = "ratatui", version = "0.24.0", features = ["crossterm"], default_features = false } crossterm = "0.27.0" anyhow = "1.0.75" dns-lookup = "2.0.0" chrono = "0.4.31" itertools = "0.12.0" shadow-rs = "0.24.1" const_format = "0.2.32" clap = { version = "4.4.10", features = ["derive"] } [build-dependencies] shadow-rs = "0.24.1" gping-1.16.0/build.rs000064400000000000000000000000761046102023000124650ustar 00000000000000fn main() -> shadow_rs::SdResult<()> { shadow_rs::new() } gping-1.16.0/readme.md000064400000000000000000000107061046102023000126000ustar 00000000000000# gping 🚀 [![Crates.io](https://img.shields.io/crates/v/gping.svg)](https://crates.io/crates/gping) [![Actions Status](https://github.com/orf/gping/workflows/CI/badge.svg)](https://github.com/orf/gping/actions) Ping, but with a graph. ![](./images/readme-example.gif) Comes with the following super-powers: * Graph the ping time for multiple hosts * Graph the _execution time_ for commands via the `--cmd` flag * Custom colours * Windows, Mac and Linux support Table of Contents ================= * [Install :cd:](#install-cd) * [Usage :saxophone:](#usage-saxophone) Packaging status # Install :cd: * macOS * [Homebrew](https://formulae.brew.sh/formula/gping#default): `brew install gping` * [MacPorts](https://ports.macports.org/port/gping/): `sudo port install gping` * Linux (Homebrew): `brew install gping` * CentOS (and other distributions with an old glibc): Download the MUSL build from the latest release * Windows/ARM: * Scoop: `scoop install gping` * Chocolatey: `choco install gping` * Download the latest release from [the github releases page](https://github.com/orf/gping/releases) * Fedora ([COPR](https://copr.fedorainfracloud.org/coprs/atim/gping/)): `sudo dnf copr enable atim/gping -y && sudo dnf install gping` * Cargo (**This requires `rustc` version 1.67.0 or greater**): `cargo install gping` * Arch Linux: `pacman -S gping` * Ubuntu/Debian ([Azlux's repo](http://packages.azlux.fr/)): ```bash echo "deb [signed-by=/usr/share/keyrings/azlux-archive-keyring.gpg] http://packages.azlux.fr/debian/ stable main" | sudo tee /etc/apt/sources.list.d/azlux.list sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg sudo apt update sudo apt install gping ``` * Gentoo ([dm9pZCAq overlay](https://github.com/gentoo-mirror/dm9pZCAq)): ```sh sudo eselect repository enable dm9pZCAq sudo emerge --sync dm9pZCAq sudo emerge net-misc/gping::dm9pZCAq ``` * FreeBSD: * [pkg](https://www.freshports.org/net-mgmt/gping/): `pkg install gping` * [ports](https://cgit.freebsd.org/ports/tree/net-mgmt/gping) `cd /usr/ports/net-mgmt/gping; make install clean` * Docker: ```sh # Check all options docker run --rm -ti --network host ghcr.io/orf/gping:gping-v1.15.1 --help # Ping google.com docker run --rm -ti --network host ghcr.io/orf/gping:gping-v1.15.1 google.com ``` # Usage :saxophone: Just run `gping [host]`. `host` can be a command like `curl google.com` if the `--cmd` flag is used. You can also use shorthands like `aws:eu-west-1` or `aws:ca-central-1` to ping specific cloud regions. Only `aws` is currently supported. ```bash $ gping --help Ping, but with a graph. Usage: gping [OPTIONS] [HOSTS_OR_COMMANDS]... Arguments: [HOSTS_OR_COMMANDS]... Hosts or IPs to ping, or commands to run if --cmd is provided. Can use cloud shorthands like aws:eu-west-1. Options: --cmd Graph the execution time for a list of commands rather than pinging hosts -n, --watch-interval Watch interval seconds (provide partial seconds like '0.5'). Default for ping is 0.2, default for cmd is 0.5. -b, --buffer Determines the number of seconds to display in the graph. [default: 30] -4 Resolve ping targets to IPv4 address -6 Resolve ping targets to IPv6 address -i, --interface Interface to use when pinging -s, --simple-graphics Uses dot characters instead of braille --vertical-margin Vertical margin around the graph (top and bottom) [default: 1] --horizontal-margin Horizontal margin around the graph (left and right) [default: 0] -c, --color Assign color to a graph entry. This option can be defined more than once as a comma separated string, and the order which the colors are provided will be matched against the hosts or commands passed to gping. Hexadecimal RGB color codes are accepted in the form of '#RRGGBB' or the following color names: 'black', 'red', 'green', 'yellow', 'blue', 'magenta','cyan', 'gray', 'dark-gray', 'light-red', 'light-green', 'light-yellow', 'light-blue', 'light-magenta', 'light-cyan', and 'white' -h, --help Print help information -V, --version Print version information --clear Clear the graph from the terminal after closing the program ``` gping-1.16.0/src/colors.rs000064400000000000000000000026111046102023000134530ustar 00000000000000use std::{iter::Iterator, ops::RangeFrom, str::FromStr}; use anyhow::{anyhow, Result}; use tui::style::Color; pub struct Colors { already_used: Vec, color_names: T, indices: RangeFrom, } impl From for Colors { fn from(color_names: T) -> Self { Self { already_used: Vec::new(), color_names, indices: 2.., } } } impl<'a, T> Iterator for Colors where T: Iterator, { type Item = Result; fn next(&mut self) -> Option { match self.color_names.next() { Some(name) => match Color::from_str(name) { Ok(color) => { if !self.already_used.contains(&color) { self.already_used.push(color); } Some(Ok(color)) } error => Some(error.map_err(|err| { anyhow!(err).context(format!("Invalid color code: `{}`", name)) })), }, None => loop { let index = unsafe { self.indices.next().unwrap_unchecked() }; let color = Color::Indexed(index); if !self.already_used.contains(&color) { self.already_used.push(color); break Some(Ok(color)); } }, } } } gping-1.16.0/src/lib.rs000064400000000000000000000000241046102023000127140ustar 00000000000000pub mod region_map; gping-1.16.0/src/main.rs000064400000000000000000000445361046102023000131120ustar 00000000000000use crate::plot_data::PlotData; use anyhow::{anyhow, Result}; use chrono::prelude::*; use clap::Parser; use crossterm::event::KeyModifiers; use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen}; use crossterm::{ event::{self, Event as CEvent, KeyCode}, execute, terminal::{disable_raw_mode, enable_raw_mode, SetSize}, }; use dns_lookup::lookup_host; use itertools::{Itertools, MinMaxResult}; use pinger::{ping_with_interval, PingResult}; use std::io; use std::io::BufWriter; use std::iter; use std::net::IpAddr; use std::ops::Add; use std::process::{Command, ExitStatus, Stdio}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::Sender; use std::sync::{mpsc, Arc}; use std::thread; use std::thread::{sleep, JoinHandle}; use std::time::{Duration, Instant}; use tui::backend::{Backend, CrosstermBackend}; use tui::layout::{Constraint, Direction, Layout}; use tui::style::{Color, Style}; use tui::text::Span; use tui::widgets::{Axis, Block, Borders, Chart, Dataset}; use tui::Terminal; mod colors; mod plot_data; mod region_map; use colors::Colors; use shadow_rs::{formatcp, shadow}; shadow!(build); const VERSION_INFO: &str = formatcp!( r#"{} commit_hash: {} build_time: {} build_env: {},{}"#, build::PKG_VERSION, build::SHORT_COMMIT, build::BUILD_TIME, build::RUST_VERSION, build::RUST_CHANNEL ); #[derive(Parser, Debug)] #[command(author, name = "gping", about = "Ping, but with a graph.", version = VERSION_INFO)] struct Args { #[arg( long, help = "Graph the execution time for a list of commands rather than pinging hosts" )] cmd: bool, #[arg( short = 'n', long, help = "Watch interval seconds (provide partial seconds like '0.5'). Default for ping is 0.2, default for cmd is 0.5." )] watch_interval: Option, #[arg( help = "Hosts or IPs to ping, or commands to run if --cmd is provided. Can use cloud shorthands like aws:eu-west-1." )] hosts_or_commands: Vec, #[arg( short, long, default_value = "30", help = "Determines the number of seconds to display in the graph." )] buffer: u64, /// Resolve ping targets to IPv4 address #[arg(short = '4', conflicts_with = "ipv6")] ipv4: bool, /// Resolve ping targets to IPv6 address #[arg(short = '6', conflicts_with = "ipv4")] ipv6: bool, /// Interface to use when pinging. #[arg(short = 'i', long)] interface: Option, #[arg(short = 's', long, help = "Uses dot characters instead of braille")] simple_graphics: bool, #[arg( long, help = "Vertical margin around the graph (top and bottom)", default_value = "1" )] vertical_margin: u16, #[arg( long, help = "Horizontal margin around the graph (left and right)", default_value = "0" )] horizontal_margin: u16, #[arg( name = "color", short = 'c', long = "color", use_value_delimiter = true, value_delimiter = ',', help = r#"Assign color to a graph entry. This option can be defined more than once as a comma separated string, and the order which the colors are provided will be matched against the hosts or commands passed to gping. Hexadecimal RGB color codes are accepted in the form of '#RRGGBB' or the following color names: 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'gray', 'dark-gray', 'light-red', 'light-green', 'light-yellow', 'light-blue', 'light-magenta', 'light-cyan', and 'white'"# )] color_codes_or_names: Vec, #[arg( name = "clear", long = "clear", help = "\ Clear the graph from the terminal after closing the program\ ", action )] clear: bool, } struct App { data: Vec, display_interval: chrono::Duration, started: chrono::DateTime, } impl App { fn new(data: Vec, buffer: u64) -> Self { App { data, display_interval: chrono::Duration::from_std(Duration::from_secs(buffer)).unwrap(), started: Local::now(), } } fn update(&mut self, host_idx: usize, item: Option) { let host = &mut self.data[host_idx]; host.update(item); } fn y_axis_bounds(&self) -> [f64; 2] { // Find the Y axis bounds for our chart. // This is trickier than the x-axis. We iterate through all our PlotData structs // and find the min/max of all the values. Then we add a 10% buffer to them. let (min, max) = match self .data .iter() .flat_map(|b| b.data.as_slice()) .map(|v| v.1) .filter(|v| !v.is_nan()) .minmax() { MinMaxResult::NoElements => (f64::INFINITY, 0_f64), MinMaxResult::OneElement(elm) => (elm, elm), MinMaxResult::MinMax(min, max) => (min, max), }; // Add a 10% buffer to the top and bottom let max_10_percent = (max * 10_f64) / 100_f64; let min_10_percent = (min * 10_f64) / 100_f64; [min - min_10_percent, max + max_10_percent] } fn x_axis_bounds(&self) -> [f64; 2] { let now = Local::now(); let now_idx; let before_idx; if (now - self.started) < self.display_interval { now_idx = (self.started + self.display_interval).timestamp_millis() as f64 / 1_000f64; before_idx = self.started.timestamp_millis() as f64 / 1_000f64; } else { now_idx = now.timestamp_millis() as f64 / 1_000f64; let before = now - self.display_interval; before_idx = before.timestamp_millis() as f64 / 1_000f64; } [before_idx, now_idx] } fn x_axis_labels(&self, bounds: [f64; 2]) -> Vec { let lower_utc = NaiveDateTime::from_timestamp_opt(bounds[0] as i64, 0) .expect("Error parsing x-axis bounds 0"); let upper_utc = NaiveDateTime::from_timestamp_opt(bounds[1] as i64, 0) .expect("Error parsing x-asis bounds 1"); let lower = Local::from_utc_datetime(&Local, &lower_utc); let upper = Local::from_utc_datetime(&Local, &upper_utc); let diff = (upper - lower) / 2; let midpoint = lower + diff; vec![ Span::raw(format!("{:?}", lower.time())), Span::raw(format!("{:?}", midpoint.time())), Span::raw(format!("{:?}", upper.time())), ] } fn y_axis_labels(&self, bounds: [f64; 2]) -> Vec { // Create 7 labels for our y axis, based on the y-axis bounds we computed above. let min = bounds[0]; let max = bounds[1]; let difference = max - min; let num_labels = 7; // Split difference into one chunk for each of the 7 labels let increment = Duration::from_micros((difference / num_labels as f64) as u64); let duration = Duration::from_micros(min as u64); (0..num_labels) .map(|i| Span::raw(format!("{:?}", duration.add(increment * i)))) .collect() } } #[derive(Debug)] enum Update { Result(Duration), Timeout, Unknown, Terminated(ExitStatus, String), } impl From for Update { fn from(result: PingResult) -> Self { match result { PingResult::Pong(duration, _) => Update::Result(duration), PingResult::Timeout(_) => Update::Timeout, PingResult::Unknown(_) => Update::Unknown, PingResult::PingExited(e, stderr) => Update::Terminated(e, stderr), } } } #[derive(Debug)] enum Event { Update(usize, Update), Terminate, Render, } fn start_render_thread( kill_event: Arc, cmd_tx: Sender, ) -> JoinHandle> { thread::spawn(move || { while !kill_event.load(Ordering::Acquire) { sleep(Duration::from_millis(250)); cmd_tx.send(Event::Render)?; } Ok(()) }) } fn start_cmd_thread( watch_cmd: &str, host_id: usize, watch_interval: Option, cmd_tx: Sender, kill_event: Arc, ) -> JoinHandle> { let mut words = watch_cmd.split_ascii_whitespace(); let cmd = words .next() .expect("Must specify a command to watch") .to_string(); let cmd_args = words.map(|w| w.to_string()).collect::>(); let interval = Duration::from_millis((watch_interval.unwrap_or(0.5) * 1000.0) as u64); // Pump cmd watches into the queue thread::spawn(move || -> Result<()> { while !kill_event.load(Ordering::Acquire) { let start = Instant::now(); let mut child = Command::new(&cmd) .args(&cmd_args) .stderr(Stdio::null()) .stdout(Stdio::null()) .spawn()?; let status = child.wait()?; let duration = start.elapsed(); let update = if status.success() { Update::Result(duration) } else { Update::Timeout }; cmd_tx.send(Event::Update(host_id, update))?; sleep(interval); } Ok(()) }) } fn start_ping_thread( host: String, host_id: usize, watch_interval: Option, ping_tx: Sender, kill_event: Arc, interface: Option, ) -> Result>> { let interval = Duration::from_millis((watch_interval.unwrap_or(0.2) * 1000.0) as u64); // Pump ping messages into the queue let stream = ping_with_interval(host, interval, interface)?; Ok(thread::spawn(move || -> Result<()> { while !kill_event.load(Ordering::Acquire) { match stream.recv() { Ok(v) => { ping_tx.send(Event::Update(host_id, v.into()))?; } Err(_) => { // Stream closed, just break return Ok(()); } } } Ok(()) })) } fn get_host_ipaddr(host: &str, force_ipv4: bool, force_ipv6: bool) -> Result { let ipaddr: Vec = match lookup_host(host) { Ok(ip) => ip, Err(e) => return Err(anyhow!("Could not resolve hostname {}", host).context(e)), }; let ipaddr = if force_ipv4 { ipaddr .iter() .find(|ip| matches!(ip, IpAddr::V4(_))) .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv4", host)) } else if force_ipv6 { ipaddr .iter() .find(|ip| matches!(ip, IpAddr::V6(_))) .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv6", host)) } else { ipaddr .first() .ok_or_else(|| anyhow!("Could not resolve '{}' to IP", host)) }; Ok(ipaddr?.to_string()) } fn main() -> Result<()> { let args: Args = Args::parse(); if args.hosts_or_commands.is_empty() { return Err(anyhow!("At least one host or command must be given (i.e gping google.com). Use --help for a full list of arguments.")); } let mut data = vec![]; let colors = Colors::from(args.color_codes_or_names.iter()); let hosts_or_commands: Vec = args .hosts_or_commands .clone() .into_iter() .map(|s| match region_map::try_host_from_cloud_region(&s) { None => s, Some(new_domain) => new_domain, }) .collect(); for (host_or_cmd, color) in hosts_or_commands.iter().zip(colors) { let color = color?; let display = match args.cmd { true => host_or_cmd.to_string(), false => format!( "{} ({})", host_or_cmd, get_host_ipaddr(host_or_cmd, args.ipv4, args.ipv6)? ), }; data.push(PlotData::new( display, args.buffer, Style::default().fg(color), args.simple_graphics, )); } let (key_tx, rx) = mpsc::channel(); let mut threads = vec![]; let killed = Arc::new(AtomicBool::new(false)); for (host_id, host_or_cmd) in hosts_or_commands.iter().cloned().enumerate() { if args.cmd { let cmd_thread = start_cmd_thread( &host_or_cmd, host_id, args.watch_interval, key_tx.clone(), std::sync::Arc::clone(&killed), ); threads.push(cmd_thread); } else { threads.push(start_ping_thread( host_or_cmd, host_id, args.watch_interval, key_tx.clone(), std::sync::Arc::clone(&killed), args.interface.clone(), )?); } } threads.push(start_render_thread( std::sync::Arc::clone(&killed), key_tx.clone(), )); let mut app = App::new(data, args.buffer); enable_raw_mode()?; let stdout = io::stdout(); let mut backend = CrosstermBackend::new(BufWriter::with_capacity(1024 * 1024 * 4, stdout)); let rect = backend.size()?; if args.clear { execute!( backend, SetSize(rect.width, rect.height), EnterAlternateScreen, )?; } else { execute!(backend, SetSize(rect.width, rect.height),)?; } let mut terminal = Terminal::new(backend)?; terminal.clear()?; // Pump keyboard messages into the queue let killed_thread = std::sync::Arc::clone(&killed); thread::spawn(move || -> Result<()> { while !killed_thread.load(Ordering::Acquire) { if event::poll(Duration::from_secs(5))? { if let CEvent::Key(key) = event::read()? { match key.code { KeyCode::Char('q') | KeyCode::Esc => { key_tx.send(Event::Terminate)?; break; } KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => { key_tx.send(Event::Terminate)?; break; } _ => {} } } } } Ok(()) }); loop { match rx.recv()? { Event::Update(host_id, update) => { match update { Update::Result(duration) => app.update(host_id, Some(duration)), Update::Timeout => app.update(host_id, None), Update::Unknown => (), Update::Terminated(e, _) if e.success() => { break; } Update::Terminated(e, stderr) => { eprintln!("There was an error running ping: {e}\nStderr: {stderr}\n"); break; } }; } Event::Render => { terminal.draw(|f| { let chunks = Layout::default() .direction(Direction::Vertical) .vertical_margin(args.vertical_margin) .horizontal_margin(args.horizontal_margin) .constraints( iter::repeat(Constraint::Length(1)) .take(app.data.len()) .chain(iter::once(Constraint::Percentage(10))) .collect::>(), ) .split(f.size()); let total_chunks = chunks.len(); let header_chunks = &chunks[0..total_chunks - 1]; let chart_chunk = &chunks[total_chunks - 1]; for (plot_data, chunk) in app.data.iter().zip(header_chunks) { let header_layout = Layout::default() .direction(Direction::Horizontal) .constraints( [ Constraint::Percentage(30), Constraint::Percentage(10), Constraint::Percentage(10), Constraint::Percentage(10), Constraint::Percentage(10), Constraint::Percentage(10), Constraint::Percentage(10), Constraint::Percentage(10), ] .as_ref(), ) .split(*chunk); for (area, paragraph) in header_layout.iter().zip(plot_data.header_stats()) { f.render_widget(paragraph, *area); } } let datasets: Vec = app.data.iter().map(|d| d.into()).collect(); let y_axis_bounds = app.y_axis_bounds(); let x_axis_bounds = app.x_axis_bounds(); let chart = Chart::new(datasets) .block(Block::default().borders(Borders::NONE)) .x_axis( Axis::default() .style(Style::default().fg(Color::Gray)) .bounds(x_axis_bounds) .labels(app.x_axis_labels(x_axis_bounds)), ) .y_axis( Axis::default() .style(Style::default().fg(Color::Gray)) .bounds(y_axis_bounds) .labels(app.y_axis_labels(y_axis_bounds)), ); f.render_widget(chart, *chart_chunk) })?; } Event::Terminate => { killed.store(true, Ordering::Release); break; } } } killed.store(true, Ordering::Relaxed); disable_raw_mode()?; execute!(terminal.backend_mut())?; terminal.show_cursor()?; let new_size = terminal.size()?; terminal.set_cursor(new_size.width, new_size.height)?; for thread in threads { thread.join().unwrap()?; } if args.clear { execute!(terminal.backend_mut(), LeaveAlternateScreen)?; }; Ok(()) } gping-1.16.0/src/plot_data.rs000064400000000000000000000076451046102023000141350ustar 00000000000000use chrono::prelude::*; use core::option::Option; use core::option::Option::{None, Some}; use core::time::Duration; use itertools::Itertools; use tui::style::Style; use tui::symbols; use tui::widgets::{Dataset, GraphType, Paragraph}; pub struct PlotData { pub display: String, pub data: Vec<(f64, f64)>, pub style: Style, buffer: chrono::Duration, simple_graphics: bool, } impl PlotData { pub fn new(display: String, buffer: u64, style: Style, simple_graphics: bool) -> PlotData { PlotData { display, data: Vec::with_capacity(150), // ringbuffer::FixedRingBuffer::new(capacity), style, buffer: chrono::Duration::seconds(buffer as i64), simple_graphics, } } pub fn update(&mut self, item: Option) { let now = Local::now(); let idx = now.timestamp_millis() as f64 / 1_000f64; match item { Some(dur) => self.data.push((idx, dur.as_micros() as f64)), None => self.data.push((idx, f64::NAN)), } // Find the last index that we should remove. let earliest_timestamp = (now - self.buffer).timestamp_millis() as f64 / 1_000f64; let last_idx = self .data .iter() .enumerate() .filter(|(_, (timestamp, _))| *timestamp < earliest_timestamp) .map(|(idx, _)| idx) .last(); if let Some(idx) = last_idx { self.data.drain(0..idx).for_each(drop) } } pub fn header_stats(&self) -> Vec { let ping_header = Paragraph::new(self.display.clone()).style(self.style); let items: Vec<&f64> = self .data .iter() .filter(|(_, x)| !x.is_nan()) .map(|(_, v)| v) .sorted_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) .collect(); if items.is_empty() { return vec![ping_header]; } let min = **items.first().unwrap(); let max = **items.last().unwrap(); let avg = items.iter().copied().sum::() / items.len() as f64; let jtr = items .iter() .zip(items.iter().skip(1)) .map(|(&prev, &curr)| (curr - prev).abs()) .sum::() / (items.len() - 1) as f64; let percentile_position = 0.95 * items.len() as f32; let rounded_position = percentile_position.round() as usize; let p95 = items.get(rounded_position).map(|i| **i).unwrap_or(0f64); // count timeouts let to = self.data.iter().filter(|(_, x)| x.is_nan()).count(); let last = self.data.last().unwrap_or(&(0f64, 0f64)).1; vec![ ping_header, Paragraph::new(format!("last {:?}", Duration::from_micros(last as u64))) .style(self.style), Paragraph::new(format!("min {:?}", Duration::from_micros(min as u64))) .style(self.style), Paragraph::new(format!("max {:?}", Duration::from_micros(max as u64))) .style(self.style), Paragraph::new(format!("avg {:?}", Duration::from_micros(avg as u64))) .style(self.style), Paragraph::new(format!("jtr {:?}", Duration::from_micros(jtr as u64))) .style(self.style), Paragraph::new(format!("p95 {:?}", Duration::from_micros(p95 as u64))) .style(self.style), Paragraph::new(format!("t/o {to:?}")).style(self.style), ] } } impl<'a> From<&'a PlotData> for Dataset<'a> { fn from(plot: &'a PlotData) -> Self { let slice = plot.data.as_slice(); Dataset::default() .marker(if plot.simple_graphics { symbols::Marker::Dot } else { symbols::Marker::Braille }) .style(plot.style) .graph_type(GraphType::Line) .data(slice) } } gping-1.16.0/src/region_map.rs000064400000000000000000000012641046102023000142750ustar 00000000000000type Host = String; pub fn try_host_from_cloud_region(query: &str) -> Option { match query.split_once(':') { Some(("aws", region)) => Some(format!("ec2.{region}.amazonaws.com")), _ => None, } } #[cfg(test)] mod tests { use super::*; #[test] fn test_host_from_aws() { assert_eq!( try_host_from_cloud_region("aws:eu-west-1"), Some("ec2.eu-west-1.amazonaws.com".to_string()) ); } #[test] fn test_host_from_foo() { assert_eq!(try_host_from_cloud_region("foo:bar"), None); } #[test] fn test_invalid_input() { assert_eq!(try_host_from_cloud_region("foo"), None); } }