Compare commits

...

10 Commits

Author SHA1 Message Date
Greg Shuflin
624f84f7be Fix fmt lints 2025-07-21 23:10:05 -07:00
Greg Shuflin
b26c568f4f time formatting 2025-07-21 23:02:37 -07:00
Greg Shuflin
5b752f10b8 Move main logic into a process_post method 2025-07-21 19:33:31 -07:00
Greg Shuflin
c3e332113e Refactor struct 2025-07-21 19:30:40 -07:00
Greg Shuflin
54d86d7e5f move other color thing into util 2025-07-21 19:26:55 -07:00
Greg Shuflin
49b51864c4 util mod 2025-07-21 19:17:34 -07:00
Greg Shuflin
7a6646c355 Add ratatui 2025-07-21 19:17:02 -07:00
Greg Shuflin
b2dffd4df0 Move some code into a post module 2025-07-21 19:13:19 -07:00
Greg Shuflin
a80e84f0d3 Upgrade colored 2025-07-13 22:45:37 -07:00
Greg Shuflin
8bf90c4f31 cargo upgrade packages 2025-07-13 22:45:15 -07:00
5 changed files with 796 additions and 262 deletions

480
Cargo.lock generated
View File

@@ -26,6 +26,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "anstream"
version = "0.6.19"
@@ -94,7 +100,7 @@ dependencies = [
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -122,8 +128,10 @@ dependencies = [
"futures-util",
"log",
"rand",
"ratatui",
"serde",
"serde_json",
"time",
"tokio",
"tokio-tungstenite",
"url",
@@ -141,6 +149,21 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cassowary"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "castaway"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
dependencies = [
"rustversion",
]
[[package]]
name = "cc"
version = "1.2.29"
@@ -204,14 +227,27 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "colored"
version = "2.2.0"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
dependencies = [
"lazy_static",
"windows-sys 0.59.0",
]
[[package]]
name = "compact_str"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
dependencies = [
"castaway",
"cfg-if",
"itoa",
"rustversion",
"ryu",
"static_assertions",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -237,6 +273,31 @@ dependencies = [
"libc",
]
[[package]]
name = "crossterm"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [
"bitflags",
"crossterm_winapi",
"mio",
"parking_lot",
"rustix 0.38.44",
"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 = "crypto-common"
version = "0.1.6"
@@ -247,12 +308,56 @@ dependencies = [
"typenum",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "data-encoding"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]]
name = "deranged"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
]
[[package]]
name = "digest"
version = "0.10.7"
@@ -274,6 +379,12 @@ dependencies = [
"syn",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "env_logger"
version = "0.10.2"
@@ -287,6 +398,12 @@ dependencies = [
"termcolor",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.13"
@@ -294,7 +411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -309,6 +426,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foreign-types"
version = "0.3.2"
@@ -416,6 +539,17 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "hashbrown"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
name = "heck"
version = "0.5.0"
@@ -537,6 +671,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.0.3"
@@ -558,6 +698,25 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "indoc"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
[[package]]
name = "instability"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a"
dependencies = [
"darling",
"indoc",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "io-uring"
version = "0.7.8"
@@ -586,24 +745,33 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
@@ -632,6 +800,15 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "lru"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
dependencies = [
"hashbrown",
]
[[package]]
name = "memchr"
version = "2.7.5"
@@ -654,6 +831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"log",
"wasi 0.11.1+wasi-snapshot-preview1",
"windows-sys 0.59.0",
]
@@ -675,6 +853,12 @@ dependencies = [
"tempfile",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "object"
version = "0.36.7"
@@ -760,9 +944,15 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -796,6 +986,12 @@ dependencies = [
"zerovec",
]
[[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.21"
@@ -859,6 +1055,27 @@ dependencies = [
"getrandom 0.2.16",
]
[[package]]
name = "ratatui"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
dependencies = [
"bitflags",
"cassowary",
"compact_str",
"crossterm",
"indoc",
"instability",
"itertools",
"lru",
"paste",
"strum",
"unicode-segmentation",
"unicode-truncate",
"unicode-width 0.2.0",
]
[[package]]
name = "redox_syscall"
version = "0.5.13"
@@ -903,6 +1120,19 @@ version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
]
[[package]]
name = "rustix"
version = "1.0.7"
@@ -912,10 +1142,16 @@ dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"linux-raw-sys 0.9.4",
"windows-sys 0.59.0",
]
[[package]]
name = "rustversion"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "ryu"
version = "1.0.20"
@@ -1009,6 +1245,27 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.5"
@@ -1046,12 +1303,40 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[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.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "2.0.104"
@@ -1083,8 +1368,8 @@ dependencies = [
"fastrand",
"getrandom 0.3.3",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"rustix 1.0.7",
"windows-sys 0.59.0",
]
[[package]]
@@ -1116,6 +1401,25 @@ dependencies = [
"syn",
]
[[package]]
name = "time"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"num-conv",
"powerfmt",
"serde",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "tinystr"
version = "0.8.1"
@@ -1213,6 +1517,35 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-truncate"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
dependencies = [
"itertools",
"unicode-segmentation",
"unicode-width 0.1.14",
]
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-width"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]]
name = "url"
version = "2.5.4"
@@ -1269,6 +1602,22 @@ dependencies = [
"wit-bindgen-rt",
]
[[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-util"
version = "0.1.9"
@@ -1278,13 +1627,19 @@ dependencies = [
"windows-sys 0.59.0",
]
[[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 = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -1293,7 +1648,16 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.2",
]
[[package]]
@@ -1302,14 +1666,30 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"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-targets"
version = "0.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
]
[[package]]
@@ -1318,48 +1698,96 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[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_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[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_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[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_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[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_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"

View File

@@ -4,14 +4,16 @@ version = "0.1.0"
edition = "2024"
[dependencies]
tokio = { version = "1.0", features = ["full"] }
tokio = { version = "1.46", features = ["full"] }
tokio-tungstenite = { version = "0.20", features = ["native-tls"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
colored = "2.0"
clap = { version = "4.0", features = ["derive"] }
colored = "3.0.0"
clap = { version = "4.5", features = ["derive"] }
futures-util = "0.3"
url = "2.4"
url = "2.5"
log = "0.4"
env_logger = "0.10"
rand = "0.8"
ratatui = "0.29.0"
time = "0.3.41"

View File

@@ -2,32 +2,15 @@ use clap::Parser;
use colored::*;
use futures_util::StreamExt;
use log::{debug, warn};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use time::UtcDateTime;
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
#[derive(Debug, Deserialize, Serialize)]
struct Post {
kind: Option<String>,
#[serde(rename = "type")]
type_: Option<String>,
time_us: Option<u64>,
did: Option<String>,
commit: Option<CommitData>,
}
mod post;
mod util;
#[derive(Debug, Deserialize, Serialize)]
struct CommitData {
#[serde(rename = "type")]
type_: Option<String>,
operation: Option<String>,
record: Option<RecordData>,
}
#[derive(Debug, Deserialize, Serialize)]
struct RecordData {
text: Option<String>,
}
use post::*;
use util::create_color_from_hsv;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
@@ -53,226 +36,160 @@ struct Args {
count: Option<usize>,
}
struct BlueskyFirehosePrinter;
struct BlueskyFirehosePrinter {
websocket_url: String,
skips: Vec<String>,
onlys: Vec<String>,
count: Option<usize>,
cfilters: HashMap<String, Vec<String>>,
fkeeps: Vec<String>,
fdrops: Vec<String>,
}
impl BlueskyFirehosePrinter {
fn new() -> Self {
Self
fn new(
websocket_url: String,
skips: Vec<String>,
onlys: Vec<String>,
count: Option<usize>,
cfilters: HashMap<String, Vec<String>>,
fkeeps: Vec<String>,
fdrops: Vec<String>,
) -> Self {
Self {
websocket_url,
skips,
onlys,
count,
cfilters,
fkeeps,
fdrops,
}
}
fn hsv_to_rgb(h: f64, s: f64, v: f64) -> (u8, u8, u8) {
let c = v * s;
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
let m = v - c;
/// Process a single post and return whether to continue processing more posts
fn process_post(&self, mut post: Post, n: &mut usize) -> bool {
// Handle missing fields
post.normalize();
let (r, g, b) = if h < 60.0 {
(c, x, 0.0)
} else if h < 120.0 {
(x, c, 0.0)
} else if h < 180.0 {
(0.0, c, x)
} else if h < 240.0 {
(0.0, x, c)
} else if h < 300.0 {
(x, 0.0, c)
} else {
(c, 0.0, x)
// Apply filters
if !post.matches_only_filter(&self.onlys) {
return true; // Continue processing
}
if post.should_skip(&self.skips) {
return true; // Continue processing
}
let ts = post.get_time_us().unwrap_or(0);
let date = UtcDateTime::from_unix_timestamp((ts as i64) / 100_000)
.unwrap_or_else(|_| UtcDateTime::UNIX_EPOCH);
// Generate HSV color based on timestamp
let h = ((ts as f64 / 4.0) % 255.0) / 255.0 * 360.0;
let mut s = 0.8;
let v;
// Apply commit type filters
if !post.matches_commit_type_filters(&self.cfilters) {
return true; // Continue processing
}
// Extract and filter text
let text = match post.extract_and_filter_text(&self.fkeeps, &self.fdrops) {
Some(text) => text,
None => return true, // Text was filtered out, continue processing
};
(
((r + m) * 255.0) as u8,
((g + m) * 255.0) as u8,
((b + m) * 255.0) as u8,
)
}
// Adjust color based on post type and text
if post.get_kind() == Some("commit") {
// Adjust brightness based on text length
v = post.calculate_text_brightness(&text);
} else {
s = 0.8;
v = 120.0 / 255.0;
}
fn create_color_from_hsv(h: f64, s: f64, v: f64) -> colored::Color {
let (r, g, b) = Self::hsv_to_rgb(h, s, v);
colored::Color::TrueColor { r, g, b }
}
// Create colors
let text_color = create_color_from_hsv(h, s, v);
fn extract_post_text(&self, post: &Post) -> String {
if let Some(ref commit) = post.commit {
if let Some(ref record) = commit.record {
if let Some(ref text) = record.text {
return text.chars().take(200).collect();
}
// Info color (dimmed version)
let info_s = s * 0.1;
let info_v = (v * 0.4).max(0.3);
let info_color = create_color_from_hsv(h, info_s, info_v);
// Print colored output
let hsv_str = format!("{:.1}:{:.1}:{:.1}", h / 360.0, s, v);
let _post_id = post.get_post_id();
let type_str = post.get_type().unwrap_or("None");
let kind_str = post.get_kind().unwrap_or("None");
if post.get_type() == Some("com") {
let (commit_type, operation) = post.get_commit_info();
let commit_type = commit_type.unwrap_or("None");
let operation = operation.unwrap_or("None");
println!(
"{}{}|type:{}|{}{}{}|hsv:{} type:{} kind:{} commit.type={} operation={}",
format!("{date}").color(info_color),
"".color(info_color),
type_str.color(info_color),
text.color(text_color),
"".color(info_color),
"".color(info_color),
hsv_str.color(info_color),
type_str.color(info_color),
kind_str.color(info_color),
commit_type.color(info_color),
operation.color(info_color)
);
} else {
println!(
"{}{}|type:{}|{}{}|hsv:{} type:{} kind:{}",
format!("{date}").color(info_color),
"".color(info_color),
type_str.color(info_color),
text.color(text_color),
"".color(info_color),
hsv_str.color(info_color),
type_str.color(info_color),
kind_str.color(info_color)
);
}
if let Some(max_count) = self.count {
*n += 1;
if *n >= max_count {
return false; // Stop processing
}
}
format!("{:?}", post)
true // Continue processing
}
async fn connect_and_print(
&self,
websocket_url: &str,
skips: &[String],
onlys: &[String],
count: Option<usize>,
cfilters: &HashMap<String, Vec<String>>,
fkeeps: &[String],
fdrops: &[String],
) -> Result<(), Box<dyn std::error::Error>> {
let url = url::Url::parse(websocket_url)?;
async fn connect_and_print(&self) -> Result<(), Box<dyn std::error::Error>> {
let url = url::Url::parse(&self.websocket_url)?;
let (ws_stream, _) = connect_async(url).await?;
println!("Connected to {}", websocket_url);
println!("Connected to {}", self.websocket_url);
let (mut _write, mut read) = ws_stream.split();
let mut n = 0;
while let Some(msg) = read.next().await {
match msg {
Ok(Message::Text(text)) => {
match serde_json::from_str::<Post>(&text) {
Ok(mut post) => {
// Handle missing fields
if post.kind.is_none() {
post.kind = post.type_.clone();
}
// Apply filters
if !onlys.is_empty() {
if let Some(ref kind) = post.kind {
if !onlys.contains(kind) {
continue;
}
}
}
if let Some(ref kind) = post.kind {
if skips.contains(kind) {
continue;
}
}
let ts = post.time_us.unwrap_or(0) / 10_000;
// Generate HSV color based on timestamp
let h = ((ts as f64 / 4.0) % 255.0) / 255.0 * 360.0;
let mut s = 0.8;
let mut v = 0.8;
let mut text = String::new();
// Handle commit type filtering and text extraction
if post.kind.as_deref() == Some("commit") {
if let Some(ref commit) = post.commit {
if let Some(ref commit_type) = commit.type_ {
// Apply commit type filters
if let Some(excludes) = cfilters.get("-") {
if excludes.iter().any(|w| commit_type.contains(w)) {
continue;
}
}
if let Some(includes) = cfilters.get("+") {
if !includes.iter().any(|w| commit_type.contains(w)) {
continue;
}
}
}
if let Some(ref record) = commit.record {
if let Some(ref record_text) = record.text {
// Apply text filters
if !fdrops.is_empty() {
if fdrops.iter().any(|w| record_text.contains(w)) {
continue;
}
}
if !fkeeps.is_empty() {
if !fkeeps.iter().any(|w| record_text.contains(w)) {
continue;
}
}
text = record_text.clone();
// Adjust brightness based on text length
v = 1.0
- (text.len() as f64)
.ln()
.min(16.0 * 256.0_f64.ln())
/ (16.0 * 256.0_f64.ln());
} else {
text = format!("commit.record={:?}", record);
}
}
}
} else {
text = self.extract_post_text(&post);
s = 0.8;
v = 120.0 / 255.0;
}
// Create colors
let text_color = Self::create_color_from_hsv(h, s, v);
// Info color (dimmed version)
let info_s = s * 0.1;
let info_v = (v * 0.4).max(0.3);
let info_color = Self::create_color_from_hsv(h, info_s, info_v);
// Print colored output
let hsv_str = format!("{:.1}:{:.1}:{:.1}", h / 360.0, s, v);
let _post_id = post.did.unwrap_or_else(|| {
format!("r:{}", rand::random::<f64>())
});
let type_str = post.type_.as_deref().unwrap_or("None");
let kind_str = post.kind.as_deref().unwrap_or("None");
if post.type_.as_deref() == Some("com") {
let commit_type = post
.commit
.as_ref()
.and_then(|c| c.type_.as_deref())
.unwrap_or("None");
let operation = post
.commit
.as_ref()
.and_then(|c| c.operation.as_deref())
.unwrap_or("None");
println!(
"{}{}|type:{}|{}{}{}|hsv:{} type:{} kind:{} commit.type={} operation={}",
format!("{}", ts).color(info_color),
format!("").color(info_color),
type_str.color(info_color),
text.color(text_color),
format!("").color(info_color),
format!("").color(info_color),
hsv_str.color(info_color),
type_str.color(info_color),
kind_str.color(info_color),
commit_type.color(info_color),
operation.color(info_color)
);
} else {
println!(
"{}{}|type:{}|{}{}|hsv:{} type:{} kind:{}",
format!("{}", ts).color(info_color),
format!("").color(info_color),
type_str.color(info_color),
text.color(text_color),
format!("").color(info_color),
hsv_str.color(info_color),
type_str.color(info_color),
kind_str.color(info_color)
);
}
if let Some(max_count) = count {
n += 1;
if n >= max_count {
return Ok(());
}
}
}
Err(e) => {
debug!("JSON decode error: {}", e);
println!("err:{}", text);
Ok(Message::Text(text)) => match serde_json::from_str::<Post>(&text) {
Ok(post) => {
if !self.process_post(post, &mut n) {
return Ok(());
}
}
}
Err(e) => {
debug!("JSON decode error: {}", e);
println!("err:{}", text);
}
},
Ok(Message::Close(_)) => {
println!("WebSocket closed");
break;
@@ -347,20 +264,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
const BLUESKY_FIREHOSE_WS: &str =
"wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.post";
let printer = BlueskyFirehosePrinter::new();
let printer = BlueskyFirehosePrinter::new(
BLUESKY_FIREHOSE_WS.to_string(),
skips,
onlys,
args.count,
cfilters,
fkeeps,
fdrops,
);
match printer
.connect_and_print(
BLUESKY_FIREHOSE_WS,
&skips,
&onlys,
args.count,
&cfilters,
&fkeeps,
&fdrops,
)
.await
{
match printer.connect_and_print().await {
Ok(()) => println!("done"),
Err(e) => {
eprintln!("Error: {}", e);

160
src/post.rs Normal file
View File

@@ -0,0 +1,160 @@
use rand;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Deserialize, Serialize)]
pub struct Post {
kind: Option<String>,
#[serde(rename = "type")]
type_: Option<String>,
time_us: Option<u64>,
did: Option<String>,
commit: Option<CommitData>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct CommitData {
#[serde(rename = "type")]
type_: Option<String>,
operation: Option<String>,
record: Option<RecordData>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RecordData {
text: Option<String>,
}
impl Post {
/// Normalize the post by ensuring kind field is set
pub fn normalize(&mut self) {
if self.kind.is_none() {
self.kind = self.type_.clone();
}
}
/// Get the type of this post
pub fn get_type(&self) -> Option<&str> {
self.type_.as_deref()
}
/// Get the kind of this post
pub fn get_kind(&self) -> Option<&str> {
self.kind.as_deref()
}
/// Get the timestamp in microseconds
pub fn get_time_us(&self) -> Option<u64> {
self.time_us
}
/// Get the DID or generate a random one
pub fn get_post_id(&self) -> String {
self.did
.clone()
.unwrap_or_else(|| format!("r:{}", rand::random::<f64>()))
}
/// Check if this post matches the "only" filter
pub fn matches_only_filter(&self, onlys: &[String]) -> bool {
if onlys.is_empty() {
return true;
}
if let Some(ref kind) = self.kind {
onlys.contains(kind)
} else {
false
}
}
/// Check if this post should be skipped
pub fn should_skip(&self, skips: &[String]) -> bool {
if let Some(ref kind) = self.kind {
skips.contains(kind)
} else {
false
}
}
/// Check if this post matches commit type filters
pub fn matches_commit_type_filters(&self, cfilters: &HashMap<String, Vec<String>>) -> bool {
if self.kind.as_deref() != Some("commit") {
return true; // Non-commit posts pass commit filters
}
if let Some(ref commit) = self.commit {
if let Some(ref commit_type) = commit.type_ {
// Apply commit type filters
if let Some(excludes) = cfilters.get("-") {
if excludes.iter().any(|w| commit_type.contains(w)) {
return false;
}
}
if let Some(includes) = cfilters.get("+") {
if !includes.iter().any(|w| commit_type.contains(w)) {
return false;
}
}
}
}
true
}
/// Check if this post matches text filters and return the extracted text
pub fn extract_and_filter_text(&self, fkeeps: &[String], fdrops: &[String]) -> Option<String> {
if self.kind.as_deref() == Some("commit") {
if let Some(ref commit) = self.commit {
if let Some(ref record) = commit.record {
if let Some(ref record_text) = record.text {
// Apply text filters
if !fdrops.is_empty() {
if fdrops.iter().any(|w| record_text.contains(w)) {
return None;
}
}
if !fkeeps.is_empty() {
if !fkeeps.iter().any(|w| record_text.contains(w)) {
return None;
}
}
return Some(record_text.clone());
} else {
return Some(format!("commit.record={:?}", record));
}
}
}
} else {
return Some(self.extract_text());
}
None
}
/// Get commit information for display
pub fn get_commit_info(&self) -> (Option<&str>, Option<&str>) {
if let Some(ref commit) = self.commit {
(commit.type_.as_deref(), commit.operation.as_deref())
} else {
(None, None)
}
}
/// Calculate color brightness based on text length
pub fn calculate_text_brightness(&self, text: &str) -> f64 {
1.0 - (text.len() as f64).ln().min(16.0 * 256.0_f64.ln()) / (16.0 * 256.0_f64.ln())
}
pub fn extract_text(&self) -> String {
if let Some(ref commit) = self.commit {
if let Some(ref record) = commit.record {
if let Some(ref text) = record.text {
return text.chars().take(200).collect();
}
}
}
format!("{:?}", self)
}
}

30
src/util.rs Normal file
View File

@@ -0,0 +1,30 @@
pub fn hsv_to_rgb(h: f64, s: f64, v: f64) -> (u8, u8, u8) {
let c = v * s;
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
let m = v - c;
let (r, g, b) = if h < 60.0 {
(c, x, 0.0)
} else if h < 120.0 {
(x, c, 0.0)
} else if h < 180.0 {
(0.0, c, x)
} else if h < 240.0 {
(0.0, x, c)
} else if h < 300.0 {
(x, 0.0, c)
} else {
(c, 0.0, x)
};
(
((r + m) * 255.0) as u8,
((g + m) * 255.0) as u8,
((b + m) * 255.0) as u8,
)
}
pub fn create_color_from_hsv(h: f64, s: f64, v: f64) -> colored::Color {
let (r, g, b) = hsv_to_rgb(h, s, v);
colored::Color::TrueColor { r, g, b }
}