This commit is contained in:
Biedermann Steve 2024-05-08 11:52:34 +02:00
commit e3dceb8e20
8 changed files with 1343 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

753
Cargo.lock generated Normal file
View File

@ -0,0 +1,753 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "anyhow"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[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.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
dependencies = [
"rustversion",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "compact_str"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
dependencies = [
"castaway",
"cfg-if",
"itoa",
"ryu",
"static_assertions",
]
[[package]]
name = "crossterm"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [
"bitflags 1.3.2",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [
"bitflags 2.5.0",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
"winapi",
]
[[package]]
name = "either"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
name = "indoc"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "libc"
version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[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.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "lru"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
dependencies = [
"hashbrown",
]
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[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",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "parking_lot"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
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.5",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "proc-macro2"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ratatui"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80"
dependencies = [
"bitflags 2.5.0",
"cassowary",
"compact_str",
"crossterm 0.27.0",
"indoc",
"itertools",
"lru",
"paste",
"stability",
"strum",
"unicode-segmentation",
"unicode-width",
]
[[package]]
name = "redox_syscall"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
dependencies = [
"bitflags 2.5.0",
]
[[package]]
name = "req"
version = "0.1.0"
dependencies = [
"anyhow",
"crossterm 0.27.0",
"indexmap",
"ratatui",
"rsn",
"serde",
"serde_yaml",
"stringlit",
"toml",
"tui",
]
[[package]]
name = "rsn"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673e95b85078271a8eb13f152a0adfd36390f2cbc7e74b0ff9dc7abb38ae9412"
dependencies = [
"serde",
"unicode-ident",
]
[[package]]
name = "rustversion"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0"
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[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.201"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "stability"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stringlit"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e80de30f6a4915f4277c75980dea789ea6b895039ce61ad2a3b79a8787e7ada1"
[[package]]
name = "strum"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "2.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "toml"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "tui"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1"
dependencies = [
"bitflags 1.3.2",
"cassowary",
"crossterm 0.25.0",
"unicode-segmentation",
"unicode-width",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "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-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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "winnow"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d"
dependencies = [
"memchr",
]
[[package]]
name = "zerocopy"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

18
Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "req"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.83"
crossterm = "0.27.0"
indexmap = { version = "2.2.6", features = ["serde"] }
ratatui = "0.26.2"
rsn = "0.1.0"
serde = { version = "1.0.201", features = ["derive"] }
serde_yaml = "0.9.34"
stringlit = "2.1.0"
toml = { version = "0.8.12", features = ["indexmap", "preserve_order"] }
tui = "0.19.0"

134
orig.md Normal file
View File

@ -0,0 +1,134 @@
# Requirements for journal-uploader
[[_TOC_]]
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED",
"MAY", and "OPTIONAL" in this document are to be interpreted as described in
[RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119).
## Purpose
The journal-uploader has two main functionalities.
- Take a stream of log messages and filter them depending on their severity
- Upload journal logs for a specified time when activated through cloud call
## Requirements
### 1. Traced Logging
#### 1.1 File Monitoring
- **1.1.1 Continuous Monitoring:** The tool **_MUST_** continuously monitor a designated directory.
#### 1.2 File Detection
- **1.2.1 Detection of New Files:** The tool **_MUST_** detect the addition of new files in the monitored directory.
- **1.2.2 Avoid Re-processing:** The tool **_MUST NOT_** process files that have already been processed.
#### 1.3 File Processing
- **1.3.1 Reading Log Messages:** When a new file is processed, each log message **_SHOULD_** be put into a buffer.
- **1.3.2 Filtering Log Messages:** The tool will search for messages of a defined priority (Trigger Priority).
Each message of this priority, as well as all messages before and after, which are inside a defined timespan, **_MUST_**
get written into a file. Every other message **_SHOULD_** gets dropped.
- **1.3.3 No Duplicate Log Messages:** The tool **_SHALL_** make sure that no log entry will be written to the file twice.
#### 1.4 Traced Log Rotation
- **1.4.1 Rotating Files:** When the size of the current traced log file exceeds a certain threshold,
it **_MUST_** be closed and a new file **_MUST_** be opened for writing.
- **1.4.2 Compression of Rotated Files:** Each traced log file **_MUST_** get compressed after it got rotated.
- **1.4.3 Rotating Directory:** When the directory size exceeds a certain threshold, the tool **_MUST_** delete the oldest
files in the directory, until the size is below the threshold again.
### 2. Remote Journal Logging
#### 2.1 Service Activation
- **2.1.1 Cloud Activation:** The remote journal logging **_SHALL_** be startable through a function call from the cloud.
The api call has the duration and max interval as arguments.
- **2.1.2 Duration:** The remote journal logging **_SHOULD_** stay active, until it reaches the specified duration.
- **2.1.3 Max Interval:** If no upload was done after the amount of time specified in max interval,
a log rotation **_SHALL_** be triggered, which will in turn get picked up by the file monitoring.
- **2.1.4 Analytics Not Accepted:** If the user has not accepted the usage of their data, the cloud call **_MUST_**
result in an error.
#### 2.2 File Monitoring
- **2.2.1 Continuous Monitoring:** The tool **_SHOULD_** continuously monitor a designated directory.
#### 2.3 File Detection
- **2.3.1 Detection of New Files:** The tool **_MUST_** detect the addition of new files in the monitored directory.
- **2.3.2 Avoid Re-processing:** The tool **_MUST NOT_** process files that have already been processed.
#### 2.4 File Processing
- **2.4.1 File Upload:** When a file gets detected, it **_SHOULD_** get uploaded to the cloud.
- **2.4.2 No Duplicate Files:** Already processed files **_MUST NOT_** get uploaded again.
- **2.4.3 Revoking Analytics:** If the user revokes the usage of their data, the service **_MAY_** continue running
but **_MUST NOT_** upload any data until the user allows the usage of their data again.
- **2.4.4 Duration Expired:** After the specified duration is expired, the service **_SHOULD_** stop uploading files.
### 3. Configuration
- **3.1 Configurable Journal Directory:** Users **_SHOULD_** be able to specify the directory to be monitored for
journal files.
- Required: This value **_MUST_** be provided as a start parameter.
- **3.2 Configurable Output Directory:** Users **_SHOULD_** be able to specify the directory into which the final files
will be written.
- Required: This value **_MUST_** be provided as a start parameter.
- **3.3 Configurable Trigger Priority:** Users **_SHOULD_** be able to specify which priority triggers the filtering.
- Default Value: _Warning_
- **3.4 Configurable Journal Context:** Users **_SHOULD_** be able to specify how many seconds of context will be added
to traced logs when encountering a trigger priority.
- Default Value: _10_
- **3.5 Configurable Max File Size:** Users **_SHOULD_** be able to specify the max file size, at which a file gets rotated.
- Default Value: _8388608_ (8 MB)
- **3.6 Configurable Max Directory Size:** Users **_SHOULD_** be able to specify the max directory size, at which a
directory gets rotated.
- Default Value: _75497472_ (72 MB)
- **3.7 Configurable File Monitoring Interval:** Users **_SHOULD_** be able to specify an interval, which **_SHOULD_** change
how long the tool waits before checking if new files are available.
- Default Value: _10_
### 4. Performance Requirements
- **4.1 Efficiency:** The tool **_SHOULD_** efficiently monitor and process files without excessive resource consumption.
- **4.2 Interval Delay:** The tool **_SHOULD_** do its work with no more than 10 seconds delay after its interval.
### 5. Data Protection
- **5.1 No Insecure Connection:** The tool **_MUST_** send data only through a secure connection.
- **5.2 GDPR compliance:** The tool **_MUST NOT_** upload data if the user has not agreed to share this information.
### 6. Testing
- **6.1 Unit Tests:** Comprehensive unit tests **_SHOULD_** be written to cover major functionalities.
- **6.2 Integration Tests:** Integration tests **_SHOULD_** be conducted to ensure all parts of the tool work together
seamlessly.
## Definitions
- Default Journal Directory: /run/log/journal/<machine_id>
- Machine ID can be found at /etc/machine-id
- Default Output Directory: /run/log/filtered-journal
## Config Defaults
- **Journal Directory**
- type: Path
- **Required**: This value **_MUST_** be provided as a start parameter.
- **Output Directory**
- type: Path
- **Required**: This value **_MUST_** be provided as a start parameter.
- **Trigger Priority**
- type: Enum
- Valid Values: _Emergency, Alert, Critical, Error, Warning, Notice, Info, Debug_
- Default Value: _Warning_
- **Journal Context**
- type: Integer
- unit: Seconds
- Default Value: _15_
- **Max File Size**
- type: Integer
- unit: Bytes
- Default Value: _8388608_ (8 MB)
- **Max Directory Size**
- type: Integer
- unit: Bytes
- Default Value: _75497472_ (72 MB)
- **File Monitoring Interval**
- type: Integer
- unit: Seconds
- Default Value: _10_

57
req.yml Normal file
View File

@ -0,0 +1,57 @@
name: journal-uploader
description: |-
The journal-uploader has two main functionalities.
- Take a stream of log messages and filter them depending on their severity
- Upload journal logs for a specified time when activated through cloud call
topics:
FEAT-1:
name: Traced Logging
subtopics:
SUB-1:
name: File Monitoring
requirements:
REQ-1:
name: Continuous Monitoring
description: The tool must continuously monitor a designated directory.
definitions:
- name: Default Journal Directory
value: /run/log/journal/<machine_id>
additional_info:
- Machine ID can be found at /etc/machine-id
- name: Default Output Directory
value: /run/log/filtered-journal
config_defaults:
- name: Journal Directory
type: Path
- name: Output Directory
type: Path
- name: Trigger Priority
type: Enum
valid_values:
- Emergency
- Alert
- Critical
- Error
- Warning
- Notice
- Info
- Debug
default_value: Warning
- name: Journal Context
type: Integer
unit: Seconds
default_value: '15'
- name: Max File Size
type: Integer
unit: Bytes
default_value: '8388608'
hint: (8 MB)
- name: Max Directory Size
type: Integer
unit: Bytes
default_value: '75497472'
hint: (72 MB)
- name: File Monitoring Interval
type: Integer
unit: Seconds
default_value: '10'

179
src/bin/tui.rs Normal file
View File

@ -0,0 +1,179 @@
use crossterm::{
event::{self, Event as CEvent, KeyCode},
execute,
style::Color,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use indexmap::indexmap;
use ratatui::{
backend::CrosstermBackend,
layout::{Constraint, Direction, Layout},
prelude::Style,
widgets::{Block, Borders, List, ListItem, ListState},
Terminal,
};
use stringlit::s;
use std::io;
use std::sync::mpsc;
use std::thread;
use std::time::{Duration, Instant};
use req::*;
enum Event<I> {
Input(I),
Tick,
}
struct App {
project: Project,
topics_list_state: ListState,
requirements_list_state: ListState,
}
impl App {
fn new(project: Project) -> App {
let mut topics_list_state = ListState::default();
topics_list_state.select(Some(0));
let mut requirements_list_state = ListState::default();
requirements_list_state.select(Some(0));
App {
project,
topics_list_state,
requirements_list_state,
}
}
fn draw(&mut self, f: &mut ratatui::Frame) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints(
[
Constraint::Percentage(10), // Project Title
Constraint::Percentage(45), // Topics List
Constraint::Percentage(45), // Requirements List
]
.as_ref(),
)
.split(f.size());
let project_title = Block::default()
.title(self.project.name.clone())
.borders(Borders::ALL);
f.render_widget(project_title, chunks[0]);
let topics: Vec<ListItem> = self
.project
.topics
.iter()
.map(|(name, _)| ListItem::new(name.clone()))
.collect();
let topics_list = List::new(topics)
.block(Block::default().borders(Borders::ALL).title("Topics"))
.highlight_style(Style::default().bg(Color::Blue.into()));
f.render_stateful_widget(topics_list, chunks[1], &mut self.topics_list_state);
let requirements: Vec<ListItem> = self
.project
.topics
.first()
.unwrap()
.1
.requirements
.iter()
.map(|(name, _)| ListItem::new(name.clone()))
.collect();
let requirements_list = List::new(requirements)
.block(Block::default().borders(Borders::ALL).title("Requirements"))
.highlight_style(Style::default().bg(Color::Yellow.into()));
f.render_stateful_widget(
requirements_list,
chunks[2],
&mut self.requirements_list_state,
);
}
fn next_topic(&mut self) {
let n = self.project.topics.len();
if let Some(i) = self.topics_list_state.selected() {
if i >= n - 1 {
self.topics_list_state.select(Some(0));
} else {
self.topics_list_state.select(Some(i + 1));
}
}
}
fn previous_topic(&mut self) {
let n = self.project.topics.len();
if let Some(i) = self.topics_list_state.selected() {
if i == 0 {
self.topics_list_state.select(Some(n - 1));
} else {
self.topics_list_state.select(Some(i - 1));
}
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let project = demo_project();
let mut app = App::new(project);
let (tx, rx) = mpsc::channel();
let tick_rate = Duration::from_millis(200);
thread::spawn(move || {
let mut last_tick = Instant::now();
loop {
let timeout = tick_rate
.checked_sub(last_tick.elapsed())
.unwrap_or_else(|| Duration::from_secs(0));
if event::poll(timeout).expect("poll works") {
if let CEvent::Key(key) = event::read().expect("read works") {
tx.send(Event::Input(key)).expect("send works");
}
}
if last_tick.elapsed() >= tick_rate {
tx.send(Event::Tick).expect("tick works");
last_tick = Instant::now();
}
}
});
loop {
terminal.draw(|f| app.draw(f))?;
match rx.recv()? {
Event::Input(event) => match event.code {
KeyCode::Char('q') => {
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?;
break;
}
KeyCode::Down => {
app.next_topic();
}
KeyCode::Up => {
app.previous_topic();
}
_ => {}
},
Event::Tick => {}
}
}
Ok(())
}

193
src/lib.rs Normal file
View File

@ -0,0 +1,193 @@
use indexmap::{indexmap, IndexMap};
use serde::{Deserialize, Serialize, Serializer};
use stringlit::s;
#[allow(dead_code)]
pub const WORD_DESCRIPTION: &str = //
r#"The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED",
"MAY", and "OPTIONAL" in this document are to be interpreted as described in
[RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119).
"#;
#[allow(dead_code)]
pub const HIGHLIGHTED_WORDS: [&str; 10] = [
"must not",
"must",
"required",
"shall not",
"shall",
"should not",
"should",
"recommended",
"may",
"optional",
];
pub fn my_trim<S>(v: &String, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(v.trim())
}
#[derive(Deserialize, Serialize)]
pub struct Requirement {
pub name: String,
#[serde(serialize_with = "my_trim")]
pub description: String,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub depends: IndexMap<String, String>,
}
#[derive(Deserialize, Serialize)]
pub struct Topic {
pub name: String,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub requirements: IndexMap<String, Requirement>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub subtopics: IndexMap<String, Topic>,
}
#[derive(Deserialize, Serialize)]
pub struct Definition {
pub name: String,
pub value: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub additional_info: Vec<String>,
}
#[derive(Deserialize, Serialize)]
pub struct ConfigDefault {
pub name: String,
#[serde(rename = "type")]
pub typ: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub valid_values: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_value: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hint: Option<String>,
}
#[derive(Deserialize, Serialize)]
pub struct Project {
pub name: String,
#[serde(serialize_with = "my_trim")]
pub description: String,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub topics: IndexMap<String, Topic>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub definitions: Vec<Definition>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub config_defaults: Vec<ConfigDefault>,
}
pub fn demo_project() -> Project {
Project {
name: s!("journal-uploader"),
description: s!(r#"
The journal-uploader has two main functionalities.
- Take a stream of log messages and filter them depending on their severity
- Upload journal logs for a specified time when activated through cloud call"#),
topics: indexmap! {
s!("FEAT-1") => Topic {
name: s!("Traced Logging"),
subtopics: indexmap! {
s!("SUB-1") => Topic {
name: s!("File Monitoring"),
requirements: indexmap! {
s!("REQ-1") => Requirement {
name: s!("Continuous Monitoring"),
description: s!(r#"The tool must continuously monitor a designated directory."#),
depends: indexmap! {}
}
},
subtopics: indexmap! {}
},
},
requirements: indexmap! {},
}
},
definitions: vec![
Definition {
name: s!("Default Journal Directory"),
value: s!("/run/log/journal/<machine_id>"),
additional_info: vec![s!("Machine ID can be found at /etc/machine-id")],
},
Definition {
name: s!("Default Output Directory"),
value: s!("/run/log/filtered-journal"),
additional_info: vec![],
},
],
config_defaults: vec![
ConfigDefault {
name: s!("Journal Directory"),
typ: s!("Path"),
unit: None,
valid_values: vec![],
default_value: None,
hint: None,
},
ConfigDefault {
name: s!("Output Directory"),
typ: s!("Path"),
unit: None,
valid_values: vec![],
default_value: None,
hint: None,
},
ConfigDefault {
name: s!("Trigger Priority"),
typ: s!("Enum"),
unit: None,
valid_values: vec![
s!("Emergency"),
s!("Alert"),
s!("Critical"),
s!("Error"),
s!("Warning"),
s!("Notice"),
s!("Info"),
s!("Debug"),
],
default_value: Some(s!("Warning")),
hint: None,
},
ConfigDefault {
name: s!("Journal Context"),
typ: s!("Integer"),
unit: Some(s!("Seconds")),
valid_values: vec![],
default_value: Some(s!("15")),
hint: None,
},
ConfigDefault {
name: s!("Max File Size"),
typ: s!("Integer"),
unit: Some(s!("Bytes")),
valid_values: vec![],
default_value: Some(s!("8388608")),
hint: Some(s!("(8 MB)")),
},
ConfigDefault {
name: s!("Max Directory Size"),
typ: s!("Integer"),
unit: Some(s!("Bytes")),
valid_values: vec![],
default_value: Some(s!("75497472")),
hint: Some(s!("(72 MB)")),
},
ConfigDefault {
name: s!("File Monitoring Interval"),
typ: s!("Integer"),
unit: Some(s!("Seconds")),
valid_values: vec![],
default_value: Some(s!("10")),
hint: None,
},
],
}
}

8
src/main.rs Normal file
View File

@ -0,0 +1,8 @@
use req::*;
fn main() -> anyhow::Result<()> {
let project = demo_project();
std::fs::write("req.yml", serde_yaml::to_string(&project)?)?;
Ok(())
}