commit e3dceb8e204904e99fb1a11a15ff2bf453a3d164 Author: Biedermann Steve Date: Wed May 8 11:52:34 2024 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..eb5b89e --- /dev/null +++ b/Cargo.lock @@ -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", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bfa3f6a --- /dev/null +++ b/Cargo.toml @@ -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" diff --git a/orig.md b/orig.md new file mode 100644 index 0000000..e873554 --- /dev/null +++ b/orig.md @@ -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 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_ + diff --git a/req.yml b/req.yml new file mode 100644 index 0000000..bd54a1e --- /dev/null +++ b/req.yml @@ -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/ + 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' diff --git a/src/bin/tui.rs b/src/bin/tui.rs new file mode 100644 index 0000000..0c1ef5b --- /dev/null +++ b/src/bin/tui.rs @@ -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 { + 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 = 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 = 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> { + 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(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..aa7fb45 --- /dev/null +++ b/src/lib.rs @@ -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(v: &String, s: S) -> Result +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, +} + +#[derive(Deserialize, Serialize)] +pub struct Topic { + pub name: String, + #[serde(default, skip_serializing_if = "IndexMap::is_empty")] + pub requirements: IndexMap, + #[serde(default, skip_serializing_if = "IndexMap::is_empty")] + pub subtopics: IndexMap, +} + +#[derive(Deserialize, Serialize)] +pub struct Definition { + pub name: String, + pub value: String, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub additional_info: Vec, +} + +#[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, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub unit: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub default_value: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hint: Option, +} + +#[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, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub definitions: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub config_defaults: Vec, +} + +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/"), + 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, + }, + ], + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..3e4eb58 --- /dev/null +++ b/src/main.rs @@ -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(()) +}