Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
909
Cargo.lock
generated
Normal file
909
Cargo.lock
generated
Normal file
@@ -0,0 +1,909 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
|
[[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 = "anyhow"
|
||||||
|
version = "1.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cassowary"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"futures-core",
|
||||||
|
"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.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||||
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||||
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
|
"equivalent",
|
||||||
|
"foldhash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"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 = "indoc"
|
||||||
|
version = "2.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.172"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
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.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"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 = "postfix-log-viewer"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"chrono",
|
||||||
|
"crossterm",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"ratatui",
|
||||||
|
"regex",
|
||||||
|
"simplelog",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ratatui"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ebc917cfb527a566c37ecb94c7e3fd098353516fb4eb6bea17015ade0182425"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cassowary",
|
||||||
|
"crossterm",
|
||||||
|
"indoc",
|
||||||
|
"itertools",
|
||||||
|
"lru",
|
||||||
|
"paste",
|
||||||
|
"strum",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
|
|
||||||
|
[[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.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
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"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simplelog"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"termcolor",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
|
||||||
|
|
||||||
|
[[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.101"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"itoa",
|
||||||
|
"libc",
|
||||||
|
"num-conv",
|
||||||
|
"num_threads",
|
||||||
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
|
||||||
|
dependencies = [
|
||||||
|
"num-conv",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
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-width"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
|
[[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.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
|
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-core"
|
||||||
|
version = "0.61.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.5",
|
||||||
|
"windows_aarch64_msvc 0.48.5",
|
||||||
|
"windows_i686_gnu 0.48.5",
|
||||||
|
"windows_i686_msvc 0.48.5",
|
||||||
|
"windows_x86_64_gnu 0.48.5",
|
||||||
|
"windows_x86_64_gnullvm 0.48.5",
|
||||||
|
"windows_x86_64_msvc 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.52.6",
|
||||||
|
"windows_aarch64_msvc 0.52.6",
|
||||||
|
"windows_i686_gnu 0.52.6",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc 0.52.6",
|
||||||
|
"windows_x86_64_gnu 0.52.6",
|
||||||
|
"windows_x86_64_gnullvm 0.52.6",
|
||||||
|
"windows_x86_64_msvc 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "postfix-log-viewer"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
crossterm = { version = "0.27.0", features = ["event-stream"] }
|
||||||
|
ratatui = { version = "0.24.0", features = ["crossterm"] }
|
||||||
|
chrono = { version = "0.4.24", features = ["serde"] }
|
||||||
|
regex = "1.9.0"
|
||||||
|
anyhow = "1.0.71"
|
||||||
|
log = "0.4.17"
|
||||||
|
simplelog = "0.12.1"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
14
sample.log
Normal file
14
sample.log
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
May 11 10:00:01 mail postfix/smtpd[12345]: connect from unknown[192.168.1.100]
|
||||||
|
May 11 10:00:02 mail postfix/smtpd[12345]: ABC12345: client=unknown[192.168.1.100]
|
||||||
|
May 11 10:00:03 mail postfix/cleanup[12346]: ABC12345: message-id=<test@example.com>
|
||||||
|
May 11 10:00:04 mail postfix/qmgr[12347]: ABC12345: from=<sender@example.com>, size=1234, nrcpt=1 (queue active)
|
||||||
|
May 11 10:00:05 mail postfix/smtp[12348]: ABC12345: to=<recipient@example.com>, relay=example.com[1.2.3.4]:25, delay=1.2, delays=0.1/0.1/0.5/0.5, dsn=2.0.0, status=sent (250 OK)
|
||||||
|
May 11 10:00:06 mail postfix/qmgr[12347]: ABC12345: removed
|
||||||
|
|
||||||
|
May 11 10:01:01 mail postfix/smtpd[12350]: connect from unknown[192.168.1.101]
|
||||||
|
May 11 10:01:02 mail postfix/smtpd[12350]: DEF67890: client=unknown[192.168.1.101]
|
||||||
|
May 11 10:01:03 mail postfix/cleanup[12351]: DEF67890: message-id=<test2@example.com>
|
||||||
|
May 11 10:01:04 mail postfix/qmgr[12347]: DEF67890: from=<another@example.com>, size=5678, nrcpt=2 (queue active)
|
||||||
|
May 11 10:01:05 mail postfix/smtp[12352]: DEF67890: to=<user1@example.com>, relay=example.com[1.2.3.4]:25, delay=1.3, dsn=2.0.0, status=sent (250 OK)
|
||||||
|
May 11 10:01:06 mail postfix/smtp[12353]: DEF67890: to=<user2@example.com>, relay=example.com[1.2.3.4]:25, delay=1.4, dsn=2.0.0, status=sent (250 OK)
|
||||||
|
May 11 10:01:07 mail postfix/qmgr[12347]: DEF67890: removed
|
||||||
85
src/main.rs
Normal file
85
src/main.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
mod models;
|
||||||
|
mod parser;
|
||||||
|
mod ui;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use crossterm::{
|
||||||
|
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyEventKind},
|
||||||
|
execute,
|
||||||
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
|
};
|
||||||
|
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||||
|
use std::{env, fs, io, time::Duration};
|
||||||
|
use ui::App;
|
||||||
|
|
||||||
|
fn run_app(
|
||||||
|
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
||||||
|
mut app: App,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let tick_rate = Duration::from_millis(250);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
terminal.draw(|f| ui::ui(f, &app))?;
|
||||||
|
|
||||||
|
if crossterm::event::poll(tick_rate)? {
|
||||||
|
if let Event::Key(key) = event::read()? {
|
||||||
|
if key.kind == KeyEventKind::Press {
|
||||||
|
app.on_key(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.should_quit {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
// Parse command line arguments
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
if args.len() != 2 {
|
||||||
|
eprintln!("Usage: {} <postfix-log-file>", args[0]);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the log file
|
||||||
|
let log_content = fs::read_to_string(&args[1])
|
||||||
|
.with_context(|| format!("Failed to read file: {}", args[1]))?;
|
||||||
|
|
||||||
|
// Parse the log file
|
||||||
|
let sessions = parser::parse_log_file(&log_content)
|
||||||
|
.context("Failed to parse log file")?;
|
||||||
|
|
||||||
|
if sessions.is_empty() {
|
||||||
|
println!("No SMTP sessions found in the log file.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup terminal
|
||||||
|
let mut stdout = io::stdout();
|
||||||
|
execute!(stdout, EnterAlternateScreen, EnableMouseCapture).context("Failed to setup terminal")?;
|
||||||
|
enable_raw_mode().context("Failed to enable raw mode")?;
|
||||||
|
|
||||||
|
let backend = CrosstermBackend::new(stdout);
|
||||||
|
let mut terminal = Terminal::new(backend).context("Failed to create terminal")?;
|
||||||
|
|
||||||
|
// Create app and run it
|
||||||
|
let app = App::new(&sessions);
|
||||||
|
let res = run_app(&mut terminal, app);
|
||||||
|
|
||||||
|
// Restore terminal
|
||||||
|
let _ = disable_raw_mode();
|
||||||
|
let _ = execute!(
|
||||||
|
terminal.backend_mut(),
|
||||||
|
LeaveAlternateScreen,
|
||||||
|
DisableMouseCapture
|
||||||
|
);
|
||||||
|
let _ = terminal.show_cursor();
|
||||||
|
|
||||||
|
if let Err(err) = res {
|
||||||
|
eprintln!("Error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
83
src/models.rs
Normal file
83
src/models.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
use chrono::NaiveDateTime;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LogEntry {
|
||||||
|
pub timestamp: NaiveDateTime,
|
||||||
|
pub hostname: String,
|
||||||
|
pub process: String,
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SMTPSession {
|
||||||
|
pub id: String,
|
||||||
|
pub entries: Vec<LogEntry>,
|
||||||
|
pub client: Option<String>,
|
||||||
|
pub from: Option<String>,
|
||||||
|
pub to: Vec<String>,
|
||||||
|
pub status: Option<String>,
|
||||||
|
pub start_time: Option<NaiveDateTime>,
|
||||||
|
pub end_time: Option<NaiveDateTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SMTPSession {
|
||||||
|
pub fn new(id: &str) -> Self {
|
||||||
|
SMTPSession {
|
||||||
|
id: id.to_string(),
|
||||||
|
entries: Vec::new(),
|
||||||
|
client: None,
|
||||||
|
from: None,
|
||||||
|
to: Vec::new(),
|
||||||
|
status: None,
|
||||||
|
start_time: None,
|
||||||
|
end_time: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_entry(&mut self, entry: LogEntry) {
|
||||||
|
if self.start_time.is_none() || entry.timestamp < self.start_time.unwrap() {
|
||||||
|
self.start_time = Some(entry.timestamp);
|
||||||
|
}
|
||||||
|
if self.end_time.is_none() || entry.timestamp > self.end_time.unwrap() {
|
||||||
|
self.end_time = Some(entry.timestamp);
|
||||||
|
}
|
||||||
|
self.entries.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_from_message(&mut self, message: &str) {
|
||||||
|
// Extract client information
|
||||||
|
if let Some(caps) = CLIENT_RE.captures(message) {
|
||||||
|
self.client = Some(caps[1].to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract from address
|
||||||
|
if let Some(caps) = FROM_RE.captures(message) {
|
||||||
|
self.from = Some(caps[1].to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract to address
|
||||||
|
if let Some(caps) = TO_RE.captures(message) {
|
||||||
|
if !self.to.contains(&caps[1].to_string()) {
|
||||||
|
self.to.push(caps[1].to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract status
|
||||||
|
if let Some(caps) = STATUS_RE.captures(message) {
|
||||||
|
self.status = Some(caps[1].to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub(crate) static ref SESSION_ID_RE: regex::Regex =
|
||||||
|
regex::Regex::new(r"\b([A-F0-9]+):\s").unwrap();
|
||||||
|
static ref CLIENT_RE: regex::Regex =
|
||||||
|
regex::Regex::new(r"client=([^,]+)").unwrap();
|
||||||
|
static ref FROM_RE: regex::Regex =
|
||||||
|
regex::Regex::new(r"from=<([^>]+)>").unwrap();
|
||||||
|
static ref TO_RE: regex::Regex =
|
||||||
|
regex::Regex::new(r"to=<([^>]+)>").unwrap();
|
||||||
|
static ref STATUS_RE: regex::Regex =
|
||||||
|
regex::Regex::new(r"status=([^\s]+)").unwrap();
|
||||||
|
}
|
||||||
65
src/parser.rs
Normal file
65
src/parser.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use crate::models::{LogEntry, SMTPSession};
|
||||||
|
use chrono::{Datelike, NaiveDateTime};
|
||||||
|
use regex::Regex;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref LOG_LINE_RE: Regex = Regex::new(
|
||||||
|
r"^(?P<month>\w+)\s+(?P<day>\d+)\s+(?P<time>\d+:\d+:\d+)\s+(?P<hostname>\S+)\s+(?P<process>\S+):\s+(?P<message>.*)$"
|
||||||
|
).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_log_file(contents: &str) -> anyhow::Result<Vec<SMTPSession>> {
|
||||||
|
let mut sessions: HashMap<String, SMTPSession> = HashMap::new();
|
||||||
|
|
||||||
|
for line in contents.lines() {
|
||||||
|
if let Some(captures) = LOG_LINE_RE.captures(line) {
|
||||||
|
let month = &captures["month"];
|
||||||
|
let day = &captures["day"];
|
||||||
|
let time = &captures["time"];
|
||||||
|
let hostname = captures["hostname"].to_string();
|
||||||
|
let process = captures["process"].to_string();
|
||||||
|
let message = captures["message"].to_string();
|
||||||
|
|
||||||
|
// Create a timestamp string in the format that chrono can parse
|
||||||
|
let year = chrono::Local::now().year();
|
||||||
|
let timestamp_str = format!("{} {} {} {}", year, month, day, time);
|
||||||
|
let timestamp = match NaiveDateTime::parse_from_str(×tamp_str, "%Y %b %d %H:%M:%S") {
|
||||||
|
Ok(ts) => ts,
|
||||||
|
Err(_) => continue, // Skip lines with invalid timestamps
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry = LogEntry {
|
||||||
|
timestamp,
|
||||||
|
hostname,
|
||||||
|
process,
|
||||||
|
message: message.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extract session ID from the message
|
||||||
|
if let Some(id_caps) = crate::models::SESSION_ID_RE.captures(&message) {
|
||||||
|
let session_id = id_caps[1].to_string();
|
||||||
|
|
||||||
|
// Get or create the session
|
||||||
|
let session = sessions.entry(session_id.clone())
|
||||||
|
.or_insert_with(|| SMTPSession::new(&session_id));
|
||||||
|
|
||||||
|
// Add the entry to the session
|
||||||
|
session.add_entry(entry);
|
||||||
|
|
||||||
|
// Update session information from the message
|
||||||
|
session.update_from_message(&message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the HashMap to a Vec and sort by start time (most recent first)
|
||||||
|
let mut sessions: Vec<SMTPSession> = sessions.into_iter().map(|(_, v)| v).collect();
|
||||||
|
sessions.sort_by(|a, b| {
|
||||||
|
let a_time = a.start_time.unwrap_or_else(|| chrono::DateTime::<chrono::Utc>::MIN_UTC.naive_local());
|
||||||
|
let b_time = b.start_time.unwrap_or_else(|| chrono::DateTime::<chrono::Utc>::MIN_UTC.naive_local());
|
||||||
|
b_time.cmp(&a_time) // Sort in descending order (newest first)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(sessions)
|
||||||
|
}
|
||||||
310
src/ui.rs
Normal file
310
src/ui.rs
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
use crate::models::SMTPSession;
|
||||||
|
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||||
|
use ratatui::{
|
||||||
|
prelude::*,
|
||||||
|
widgets::*,
|
||||||
|
Frame,
|
||||||
|
};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
pub struct App<'a> {
|
||||||
|
pub sessions: &'a [SMTPSession],
|
||||||
|
filtered_sessions: Vec<usize>,
|
||||||
|
pub selected_session: Option<usize>,
|
||||||
|
pub scroll: u16,
|
||||||
|
pub should_quit: bool,
|
||||||
|
filter_type: Option<FilterType>,
|
||||||
|
filter_text: String,
|
||||||
|
pub log_display_mode: LogDisplayMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum LogDisplayMode {
|
||||||
|
SingleLine,
|
||||||
|
MultiLine,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum FilterType {
|
||||||
|
From,
|
||||||
|
To,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> App<'a> {
|
||||||
|
pub fn new(sessions: &'a [SMTPSession]) -> Self {
|
||||||
|
let filtered_sessions = (0..sessions.len()).collect();
|
||||||
|
App {
|
||||||
|
sessions,
|
||||||
|
filtered_sessions,
|
||||||
|
selected_session: if !sessions.is_empty() { Some(0) } else { None },
|
||||||
|
scroll: 0,
|
||||||
|
should_quit: false,
|
||||||
|
filter_type: None,
|
||||||
|
filter_text: String::new(),
|
||||||
|
log_display_mode: LogDisplayMode::SingleLine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_filters(&mut self) {
|
||||||
|
self.filtered_sessions = (0..self.sessions.len())
|
||||||
|
.filter(|&i| {
|
||||||
|
let session = &self.sessions[i];
|
||||||
|
if self.filter_text.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
match self.filter_type {
|
||||||
|
Some(FilterType::From) => session.from.as_ref()
|
||||||
|
.map_or(false, |from| from.contains(&self.filter_text)),
|
||||||
|
Some(FilterType::To) => session.to.iter()
|
||||||
|
.any(|to| to.contains(&self.filter_text)),
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Reset selection if current selection is no longer in filtered list
|
||||||
|
if let Some(selected) = self.selected_session {
|
||||||
|
if !self.filtered_sessions.contains(&selected) {
|
||||||
|
self.selected_session = if !self.filtered_sessions.is_empty() {
|
||||||
|
Some(self.filtered_sessions[0])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if !self.filtered_sessions.is_empty() {
|
||||||
|
self.selected_session = Some(self.filtered_sessions[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_tick(&mut self) {
|
||||||
|
// Update app state if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_key(&mut self, key: KeyEvent) {
|
||||||
|
match key.code {
|
||||||
|
KeyCode::Char('q') => self.should_quit = true,
|
||||||
|
KeyCode::Char('c') if key.modifiers.intersects(KeyModifiers::CONTROL) => self.should_quit = true,
|
||||||
|
KeyCode::Down => {
|
||||||
|
if let Some(selected) = self.selected_session {
|
||||||
|
if selected < self.sessions.len().saturating_sub(1) {
|
||||||
|
self.selected_session = Some(selected + 1);
|
||||||
|
self.scroll = 0;
|
||||||
|
}
|
||||||
|
} else if !self.sessions.is_empty() {
|
||||||
|
self.selected_session = Some(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Up => {
|
||||||
|
if let Some(selected) = self.selected_session {
|
||||||
|
if selected > 0 {
|
||||||
|
self.selected_session = Some(selected - 1);
|
||||||
|
self.scroll = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Char('j') => {
|
||||||
|
self.scroll = self.scroll.saturating_add(1);
|
||||||
|
}
|
||||||
|
KeyCode::Char('k') => {
|
||||||
|
self.scroll = self.scroll.saturating_sub(1);
|
||||||
|
}
|
||||||
|
KeyCode::Char('l') => {
|
||||||
|
self.log_display_mode = match self.log_display_mode {
|
||||||
|
LogDisplayMode::SingleLine => LogDisplayMode::MultiLine,
|
||||||
|
LogDisplayMode::MultiLine => LogDisplayMode::SingleLine,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
KeyCode::Char('f') => {
|
||||||
|
self.filter_type = Some(FilterType::From);
|
||||||
|
self.filter_text.clear();
|
||||||
|
}
|
||||||
|
KeyCode::Char('t') => {
|
||||||
|
self.filter_type = Some(FilterType::To);
|
||||||
|
self.filter_text.clear();
|
||||||
|
}
|
||||||
|
KeyCode::Char('r') => {
|
||||||
|
self.filter_type = None;
|
||||||
|
self.filter_text.clear();
|
||||||
|
self.apply_filters();
|
||||||
|
}
|
||||||
|
KeyCode::Enter if self.filter_type.is_some() => {
|
||||||
|
self.apply_filters();
|
||||||
|
self.filter_type = None;
|
||||||
|
}
|
||||||
|
KeyCode::Char(c) if self.filter_type.is_some() => {
|
||||||
|
self.filter_text.push(c);
|
||||||
|
self.apply_filters();
|
||||||
|
}
|
||||||
|
KeyCode::Backspace if self.filter_type.is_some() => {
|
||||||
|
self.filter_text.pop();
|
||||||
|
self.apply_filters();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(f: &mut Frame, app: &App) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([Constraint::Percentage(40), Constraint::Percentage(60)].as_ref())
|
||||||
|
.split(f.size());
|
||||||
|
|
||||||
|
// Left panel - Session list
|
||||||
|
// Display filter status
|
||||||
|
let filter_status = if let Some(filter_type) = &app.filter_type {
|
||||||
|
let filter_label = match filter_type {
|
||||||
|
FilterType::From => "Filter from",
|
||||||
|
FilterType::To => "Filter to",
|
||||||
|
};
|
||||||
|
format!("{}: {}", filter_label, app.filter_text)
|
||||||
|
} else if !app.filtered_sessions.is_empty() {
|
||||||
|
format!("Filter active ({} sessions)", app.filtered_sessions.len())
|
||||||
|
} else {
|
||||||
|
String::from("No active filter")
|
||||||
|
};
|
||||||
|
|
||||||
|
let session_list: Vec<ListItem> = app
|
||||||
|
.filtered_sessions
|
||||||
|
.iter()
|
||||||
|
.map(|&i| {
|
||||||
|
let session = &app.sessions[i];
|
||||||
|
let is_selected = app.selected_session == Some(i);
|
||||||
|
let style = if is_selected {
|
||||||
|
Style::default().add_modifier(Modifier::BOLD)
|
||||||
|
} else {
|
||||||
|
Style::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let from = session.from.as_deref().unwrap_or("<unknown>");
|
||||||
|
let to_count = session.to.len();
|
||||||
|
let client = session.client.as_deref().unwrap_or("<unknown>");
|
||||||
|
let time = session.start_time
|
||||||
|
.map(|t| t.format("%Y-%m-%d %H:%M:%S").to_string())
|
||||||
|
.unwrap_or_else(|| "????-??-?? ??:??:??".to_string());
|
||||||
|
|
||||||
|
let line = format!("{} {} → {} ({} recipient{})",
|
||||||
|
time, from, client, to_count, if to_count != 1 { "s" } else { "" });
|
||||||
|
|
||||||
|
ListItem::new(Line::from(Span::styled(line, style)))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Show filter status in the title
|
||||||
|
let sessions_title = format!("Sessions {}", filter_status);
|
||||||
|
|
||||||
|
let mut state = ListState::default();
|
||||||
|
state.select(app.selected_session);
|
||||||
|
|
||||||
|
let sessions_list = List::new(session_list)
|
||||||
|
.block(Block::default().borders(Borders::ALL).title(sessions_title))
|
||||||
|
.highlight_style(Style::default().bg(Color::DarkGray));
|
||||||
|
|
||||||
|
f.render_stateful_widget(sessions_list, chunks[0], &mut state);
|
||||||
|
|
||||||
|
// Right panel - Session details
|
||||||
|
let details = if let Some(selected_idx) = app.selected_session {
|
||||||
|
if let Some(selected) = app.sessions.get(selected_idx) {
|
||||||
|
let mut lines = vec![];
|
||||||
|
|
||||||
|
// Session header
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
Span::styled("Session ID: ", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::raw(&selected.id),
|
||||||
|
]));
|
||||||
|
|
||||||
|
if let Some(from) = &selected.from {
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
Span::styled("From: ", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::raw(from),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !selected.to.is_empty() {
|
||||||
|
let to_str = selected.to.join(", ");
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
Span::styled("To: ", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::raw(to_str),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(client) = &selected.client {
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
Span::styled("Client: ", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::raw(client),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(status) = &selected.status {
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
Span::styled("Status: ", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::styled(
|
||||||
|
status,
|
||||||
|
match status.as_str() {
|
||||||
|
"sent" => Style::default().fg(Color::Green),
|
||||||
|
"deferred" => Style::default().fg(Color::Yellow),
|
||||||
|
"bounced" => Style::default().fg(Color::Red),
|
||||||
|
_ => Style::default(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a separator
|
||||||
|
lines.push(Line::from(""));
|
||||||
|
lines.push(Line::from(Span::styled("Log Entries:", Style::default().add_modifier(Modifier::BOLD))));
|
||||||
|
|
||||||
|
// Add log entries
|
||||||
|
let panel_width = chunks[1].width as usize;
|
||||||
|
for entry in &selected.entries {
|
||||||
|
let time = entry.timestamp.format("%H:%M:%S").to_string();
|
||||||
|
let log_line = format!("{} {}", time, entry.message);
|
||||||
|
|
||||||
|
match app.log_display_mode {
|
||||||
|
LogDisplayMode::SingleLine => {
|
||||||
|
// Truncate the line if it's too long
|
||||||
|
let max_len = panel_width.saturating_sub(3); // Leave room for "..."
|
||||||
|
if log_line.len() > max_len {
|
||||||
|
let truncated = log_line.chars().take(max_len).collect::<String>();
|
||||||
|
lines.push(Line::from(format!("{}...", truncated)));
|
||||||
|
} else {
|
||||||
|
lines.push(Line::from(log_line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LogDisplayMode::MultiLine => {
|
||||||
|
// Split long lines into multiple lines
|
||||||
|
let mut remaining = log_line.as_str();
|
||||||
|
while !remaining.is_empty() {
|
||||||
|
let chunk: String = remaining.chars()
|
||||||
|
.take(panel_width)
|
||||||
|
.collect();
|
||||||
|
let chunk_len = chunk.len();
|
||||||
|
lines.push(Line::from(chunk));
|
||||||
|
remaining = &remaining[chunk_len..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Paragraph::new(lines)
|
||||||
|
} else {
|
||||||
|
Paragraph::new("Invalid session selected")
|
||||||
|
}
|
||||||
|
} else if !app.filtered_sessions.is_empty() {
|
||||||
|
Paragraph::new("No matching sessions found")
|
||||||
|
} else {
|
||||||
|
Paragraph::new("No sessions available")
|
||||||
|
};
|
||||||
|
|
||||||
|
let display_mode = match app.log_display_mode {
|
||||||
|
LogDisplayMode::SingleLine => "Single-line",
|
||||||
|
LogDisplayMode::MultiLine => "Multi-line",
|
||||||
|
};
|
||||||
|
|
||||||
|
let details_block = Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title(format!("Session Details | Logs: {}", display_mode));
|
||||||
|
|
||||||
|
let details_content = details.block(details_block);
|
||||||
|
f.render_widget(details_content, chunks[1]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user