diff --git a/.clippy.toml b/.clippy.toml deleted file mode 100644 index 56ce04e..0000000 --- a/.clippy.toml +++ /dev/null @@ -1 +0,0 @@ -msrv = "1.56.1" diff --git a/.dockerignore b/.dockerignore index a86661c..7b0da2b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,6 +4,7 @@ /target/ /.cargo/ /assets/ +/config/ # Files .editorconfig diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 62e0f50..bc76e00 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ +github: orhun patreon: orhunp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d2b1be..823dad1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,15 +43,7 @@ jobs: override: true - name: Cache Cargo dependencies - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + uses: Swatinem/rust-cache@v1 - name: Build uses: actions-rs/cargo@v1 @@ -86,15 +78,7 @@ jobs: override: true - name: Cache Cargo dependencies - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + uses: Swatinem/rust-cache@v1 - name: Setup cargo-tarpaulin shell: bash @@ -165,7 +149,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: -- -D warnings + args: --tests -- -D warnings - name: Run cargo-deny uses: EmbarkStudios/cargo-deny-action@v1 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7ddd91b..7413ad5 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -22,7 +22,7 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: | orhunp/systeroid diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dd0e82..01654d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.1] - 2022-08-30 +### Updated +- Update the allowed licenses for cargo-deny +- Add metadata for cargo-binstall +- Remove unnecessary dirs dependency +- Bump dependencies + +## [0.2.0] - 2022-08-11 +### Features +- Add a configuration file ([#12](https://github.com/orhun/git-cliff/issues/12)) + - See [configuration](https://github.com/orhun/systeroid#configuration) and [`systeroid.conf`](https://github.com/orhun/systeroid/blob/main/config/systeroid.conf) +- (tui) Show deprecated values optionally via `--deprecated` flag + +### Documentation +- Update broken links + +### Miscellaneous Tasks +- Update MSRV to 1.57.0 +- Switch to Rust stable builds +- Bump dependencies +- Enable [GitHub Sponsors](https://github.com/sponsors/orhun) for funding + - Consider supporting my open source work 💖 + ## [0.1.1] - 2022-04-19 ### Added - (cli) Support explaining multiple parameters diff --git a/Cargo.lock b/Cargo.lock index 21238c5..92ba694 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -67,12 +78,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - [[package]] name = "cfg-if" version = "1.0.0" @@ -102,15 +107,15 @@ dependencies = [ [[package]] name = "colorsys" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dfdf9179d546b55ff3f88c9d93ecfaa3e9760163da5a1080af5243230dbbb70" +checksum = "52c2ad453c82bd637e3969dc52f06676610db0b20c607bf0634c7e9d840789e8" [[package]] name = "copypasta" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4423d79fed83ebd9ab81ec21fa97144300a961782158287dc9bf7eddac37ff0b" +checksum = "d7216b5c1e9ad3867252505995b02d01c6fa7e6db0d8abd42634352ef377777e" dependencies = [ "clipboard-win", "objc", @@ -122,9 +127,9 @@ dependencies = [ [[package]] name = "copypasta-ext" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2f548212db51db2dba038e6e86c98a5d4a8ab0767ab230673a28be89a45b87" +checksum = "af4c1c3f959cd8047b935d503c30e8bdfc9f7539493962ec3e45b9f0d305cbcc" dependencies = [ "copypasta", "libc", @@ -143,9 +148,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.2" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if", "crossbeam-utils", @@ -153,9 +158,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -164,25 +169,26 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.7" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" dependencies = [ + "autocfg", "cfg-if", "crossbeam-utils", - "lazy_static", "memoffset", + "once_cell", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" dependencies = [ "cfg-if", - "lazy_static", + "once_cell", ] [[package]] @@ -215,6 +221,12 @@ dependencies = [ "libloading", ] +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + [[package]] name = "downcast-rs" version = "1.2.0" @@ -223,19 +235,17 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "flate2" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ - "cfg-if", "crc32fast", - "libc", "miniz_oxide", ] @@ -256,9 +266,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", @@ -267,9 +277,9 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" dependencies = [ "aho-corasick", "bstr", @@ -289,6 +299,15 @@ dependencies = [ "walkdir", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -318,9 +337,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "lazy-bytes-cast" @@ -336,9 +355,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.119" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "libloading" @@ -352,9 +371,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] @@ -370,15 +389,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.3.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" +checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" dependencies = [ "libc", ] @@ -400,22 +419,20 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" dependencies = [ "adler", - "autocfg", ] [[package]] name = "nix" -version = "0.22.3" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", - "cc", "cfg-if", "libc", "memoffset", @@ -423,13 +440,12 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] [[package]] @@ -479,15 +495,25 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown", +] [[package]] name = "parseit" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b1326519874d2c452c203ca512d83be71b4703e8d848a9903f18771660f613" +checksum = "efbe4a78e155d43f845fb833df08c11fa29701493a9d735d5119462313e6a97c" dependencies = [ "flate2", "globwalk", @@ -497,17 +523,17 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -521,18 +547,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.15" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" dependencies = [ "autocfg", "crossbeam-deque", @@ -542,9 +568,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -554,9 +580,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.11" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -572,19 +598,20 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", "redox_syscall", + "thiserror", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -593,15 +620,25 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap", +] [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "same-file" @@ -626,18 +663,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" dependencies = [ "proc-macro2", "quote", @@ -646,9 +683,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "itoa", "ryu", @@ -657,15 +694,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "smithay-client-toolkit" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1325f292209cee78d5035530932422a30aa4c8fda1a16593ac083c1de211e68a" +checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" dependencies = [ "bitflags", "dlib", @@ -681,9 +718,9 @@ dependencies = [ [[package]] name = "smithay-clipboard" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610b551bd25378bfd2b8e7a0fcbd83d427e8f2f6a40c47ae0f70688e9949dd55" +checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" dependencies = [ "smithay-client-toolkit", "wayland-client", @@ -691,20 +728,20 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.86" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "sysctl" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1123645dfaf2b5eac6b6c88addafc359c789b8ef2a770ecaef758c1ddf363ea4" +checksum = "f99d037b2bef227ab8963f4b0acc33ecbb1f9a2e7365add7789372b387ec19e1" dependencies = [ "bitflags", "byteorder", @@ -715,7 +752,7 @@ dependencies = [ [[package]] name = "systeroid" -version = "0.1.1" +version = "0.2.1" dependencies = [ "getopts", "systeroid-core", @@ -723,13 +760,14 @@ dependencies = [ [[package]] name = "systeroid-core" -version = "0.1.1" +version = "0.2.1" dependencies = [ "colored", "dirs-next", "lazy_static", "parseit", "rayon", + "rust-ini", "serde", "serde_json", "sysctl", @@ -738,7 +776,7 @@ dependencies = [ [[package]] name = "systeroid-tui" -version = "0.1.1" +version = "0.2.1" dependencies = [ "colorsys", "copypasta-ext", @@ -764,18 +802,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487" dependencies = [ "proc-macro2", "quote", @@ -793,9 +831,9 @@ dependencies = [ [[package]] name = "tui" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fe69244ec2af261bced1d9046a6fee6c8c2a6b0228e59e5ba39bc8ba4ed729" +checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" dependencies = [ "bitflags", "cassowary", @@ -804,6 +842,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "unicode-ident" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" + [[package]] name = "unicode-segmentation" version = "1.9.0" @@ -816,12 +860,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - [[package]] name = "version_check" version = "0.9.4" @@ -841,15 +879,15 @@ dependencies = [ [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wayland-client" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91223460e73257f697d9e23d401279123d36039a3f7a449e983f123292d4458f" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" dependencies = [ "bitflags", "downcast-rs", @@ -863,9 +901,9 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" dependencies = [ "nix", "once_cell", @@ -875,9 +913,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" dependencies = [ "nix", "wayland-client", @@ -886,9 +924,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" dependencies = [ "bitflags", "wayland-client", @@ -898,9 +936,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a1ed3143f7a143187156a2ab52742e89dac33245ba505c17224df48939f9e0" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" dependencies = [ "proc-macro2", "quote", @@ -909,9 +947,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" dependencies = [ "dlib", "lazy_static", @@ -920,9 +958,9 @@ dependencies = [ [[package]] name = "which" -version = "4.2.4" +version = "4.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" dependencies = [ "either", "lazy_static", @@ -962,21 +1000,21 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "x11-clipboard" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473068b7b80ac86a18328824f1054e5e007898c47b5bbc281bd7abe32bc3653c" +checksum = "6a7468a5768fea473e6c8c0d4b60d6d7001a64acceaac267207ca0281e1337e8" dependencies = [ "xcb", ] [[package]] name = "xcb" -version = "0.10.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771e2b996df720cd1c6dd9ff90f62d91698fd3610cc078388d0564bdd6622a9c" +checksum = "b127bf5bfe9dbb39118d6567e3773d4bbc795411a8e1ef7b7e056bccac0011a9" dependencies = [ + "bitflags", "libc", - "log", "quick-xml", ] diff --git a/README.md b/README.md index 5c62561..e73f145 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ Although **systeroid** does not need the parameter section to be specified expli - [Changing the colors](#changing-the-colors) - [Viewing the parameter documentation](#viewing-the-parameter-documentation) - [Setting the refresh rate](#setting-the-refresh-rate) +- [Configuration](#configuration) - [Resources](#resources) - [References](#references) - [Logo](#logo) @@ -215,6 +216,7 @@ systeroid [options] [variable[=value] ...] --load[=] -P, --no-pager do not pipe output into a pager -v, --verbose enable verbose logging --tui show terminal user interface +-c, --config set the path of the configuration file -h, --help display this help and exit (-d) -V, --version output version information and exit ``` @@ -415,6 +417,8 @@ systeroid-tui [options] --fg-color set the foreground color [default: white] -n, --no-docs do not show the kernel documentation + --deprecated include deprecated variables while listing +-c, --config set the path of the configuration file -h, --help display this help and exit -V, --version output version information and exit ``` @@ -547,6 +551,28 @@ It is possible to specify a value in milliseconds via `--tick-rate` argument for systeroid-tui --tick-rate 500 ``` +## Configuration + +**systeroid** can be configured with a configuration file that uses the [INI format](https://en.wikipedia.org/wiki/INI_file). It can be specified via `--config` or `SYSTEROID_CONFIG` environment variable. It can also be placed in one of the following global locations: + +- `$HOME/.config/systeroid/systeroid.conf` +- `$HOME/.systeroid/systeroid.conf` + +```sh +# set the config path via argument +systeroid --config config/systeroid.conf + +# set the config path via env +SYSTEROID_CONFIG=config/systeroid.conf systeroid + +# use a global path +mkdir -p "$HOME/.config/systeroid" +cp config/systeroid.conf "$HOME/.config/systeroid" +systeroid +``` + +See the example [systeroid.conf](./config/systeroid.conf) for the configuration options. + ## Resources ### References @@ -557,9 +583,7 @@ systeroid-tui --tick-rate 500 ### Logo -**systeroid** logo was originally painted by [Ryan Tippery](https://www.ryantippery.com/about) as a part of the [Compositions](https://www.ryantippery.com/compositions/) art collection and it is put together by me using the [Filled Spots](https://www.fontspace.com/filled-spots-font-f30755) font. Shout out to Ryan for letting me use his painting for the logo! **<3** - -Check out his [store](https://www.ryantippery.com/store) for a fine piece of similar art. Kudos! +**systeroid** logo was originally painted by [Ryan Tippery](https://ryantippery.com) as a part of the [Compositions](https://ryantippery.com/art/compositions) art collection and it is put together by me using the [Filled Spots](https://www.fontspace.com/filled-spots-font-f30755) font. Shout out to Ryan for letting me use his painting for the logo! **<3** Kudos! ### Social Links @@ -570,7 +594,7 @@ Check out his [store](https://www.ryantippery.com/store) for a fine piece of sim ### Funding -If you find **systeroid** and/or other projects on my [GitHub profile](https://github.com/orhun/) useful, consider [becoming a patron](https://www.patreon.com/join/orhunp)! +If you find **systeroid** and/or other projects on my [GitHub profile](https://github.com/orhun/) useful, consider supporting me on [GitHub Sponsors](https://github.com/sponsors/orhun) or [becoming a patron](https://www.patreon.com/join/orhunp)! [![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dorhunp%26type%3Dpatrons&style=flat&logo=Patreon&labelColor=000000&color=CECDCB&logoColor=CECDCB)](https://patreon.com/join/orhunp) [![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dorhunp%26type%3Dpledges&style=flat&logo=Patreon&labelColor=000000&color=CECDCB&logoColor=CECDCB&label=)](https://patreon.com/join/orhunp) diff --git a/bors.toml b/bors.toml index 1c66d3d..cd06ebd 100644 --- a/bors.toml +++ b/bors.toml @@ -18,6 +18,6 @@ up_to_date_approvals = true delete_merged_branches = true update_base_for_deletes = true use_codeowners = true -use_squash_merge = true commit_title = "Merge ${PR_REFS}" timeout_sec = 36000 +cut_body_after = "
" diff --git a/config/systeroid.conf b/config/systeroid.conf new file mode 100644 index 0000000..ff21342 --- /dev/null +++ b/config/systeroid.conf @@ -0,0 +1,66 @@ +; systeroid ~ configuration file +; https://github.com/orhun/systeroid +; +; Each line either contains a comment or a command line argument grouped under a section. +; Run "systeroid --help" or "systeroid-tui --help" to get a list of all possible configuration options. + +[general] +; display the deprecated parameters such as base_reachable_time and retrans_time while listing +; See https://bugzilla.redhat.com/show_bug.cgi?id=152435 +display_deprecated = false +; path of the Linux kernel documentation +; this is distro dependent, systeroid checks the following locations as default: +; - /usr/share/doc/linux/ +; - /usr/share/doc/linux-doc/ +; - /usr/share/doc/linux-docs/ +; - /usr/share/doc/kernel-doc-*/Documentation/ +kernel_docs = "/usr/share/doc/linux" + +[cli] +; enable verbose logging +verbose = false +; ignore unknown variable errors +ignore_errors = true +; do not print variable after the value is set +quiet = false +; do not pipe output into a pager +; note that the default pager is less(1) and you can change it by using `PAGER` environment variable +no_pager = false +; display type for the parameter, available options are: +; - default: print the parameter name along with its value +; - name: print only the name of the parameter +; - value: print only the value of the parameter +; - binary: print only the value of the parameter without new line +display_type = "default" +; output type for the list, available options are: +; - default: print the output as is +; - tree: print the output in a tree-like format +; - json: print the output in JSON format +output_type = "default" + +[cli.colors] +; available colors are defined in https://docs.rs/colored/latest/colored/enum.Color.html +; default color for the symbols +default_color = "bright black" +; section colors +section_abi = "red" +section_fs = "green" +section_kernel = "magenta" +section_net = "blue" +section_sunrpc = "yellow" +section_user = "cyan" +section_vm = "bright red" +section_unknown = "white" + +[tui] +; tick rate of the terminal +tick_rate = 250 +; disable showing the parameter documentation +no_docs = true + +[tui.colors] +; available colors are defined in https://docs.rs/tui/latest/tui/style/enum.Color.html +; terminal foreground color +fg_color = "white" +; terminal background color +bg_color = "black" diff --git a/deny.toml b/deny.toml index a0d8927..fa3ff73 100644 --- a/deny.toml +++ b/deny.toml @@ -8,7 +8,8 @@ allow = [ "Apache-2.0", "BSL-1.0", "MPL-2.0", - "ISC" + "ISC", + "Unicode-DFS-2016" ] [sources] diff --git a/man8/systeroid-tui.8 b/man8/systeroid-tui.8 index 1f885d8..04e74b7 100644 --- a/man8/systeroid-tui.8 +++ b/man8/systeroid-tui.8 @@ -1,6 +1,6 @@ .\" Manpage for systeroid-tui -.TH SYSTEROID-TUI "8" "2022-04-19" "systeroid-tui" "System Administration" +.TH SYSTEROID-TUI "8" "2022-08-30" "systeroid-tui" "System Administration" .SH NAME systeroid-tui \- manage kernel parameters with a terminal user interface @@ -34,6 +34,9 @@ Use this option to set the foreground color. [default: white] \fB\-n\fR, \fB\-\-no\-docs\fR Do not show the kernel documentation. .TP +\fB\-\-deprecated\fR +Include deprecated variables while listing. +.TP \fB\-h\fR, \fB\-\-help\fR Display help text and exit. .TP diff --git a/man8/systeroid.8 b/man8/systeroid.8 index 5cb7bb1..b1bfda7 100644 --- a/man8/systeroid.8 +++ b/man8/systeroid.8 @@ -1,6 +1,6 @@ .\" Manpage for systeroid -.TH SYSTEROID "8" "2022-04-19" "systeroid" "System Administration" +.TH SYSTEROID "8" "2022-08-30" "systeroid" "System Administration" .SH NAME systeroid \- configure kernel parameters at runtime diff --git a/systeroid-core/Cargo.toml b/systeroid-core/Cargo.toml index 432e396..106c7a9 100644 --- a/systeroid-core/Cargo.toml +++ b/systeroid-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "systeroid-core" -version = "0.1.1" # managed by release.sh +version = "0.2.1" # managed by release.sh description = "Core library of systeroid" authors = ["Orhun Parmaksız "] license = "MIT OR Apache-2.0" @@ -12,12 +12,13 @@ edition = "2021" rust-version = "1.56.1" [dependencies] -sysctl = "0.4.4" -thiserror = "1.0.29" +sysctl = "0.5.2" +thiserror = "1.0.34" lazy_static = "1.4.0" -rayon = "1.5.2" +rayon = "1.5.3" colored = "2.0.0" -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0.79" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.85" dirs-next = "2.0.0" -parseit = { version = "0.1.0", features = ["gzip"] } +parseit = { version = "0.1.1", features = ["gzip"] } +rust-ini = "0.18.0" diff --git a/systeroid-core/src/config.rs b/systeroid-core/src/config.rs index d0e4c44..a33ed73 100644 --- a/systeroid-core/src/config.rs +++ b/systeroid-core/src/config.rs @@ -1,9 +1,26 @@ -use crate::sysctl::display::DisplayType; +use crate::error::Result; +use crate::sysctl::r#type::{DisplayType, OutputType}; use crate::sysctl::section::Section; use colored::Color; +use ini::Ini; use std::collections::HashMap; +use std::path::PathBuf; -/* Macro for the concise initialization of HashMap */ +/// Default configuration file. +pub const DEFAULT_CONFIG: &str = "systeroid.conf"; + +/// Environment variable for setting the path of the configuration file. +pub const CONFIG_ENV: &str = "SYSTEROID_CONFIG"; + +lazy_static! { + /// Default locations for the configuration file. + pub static ref DEFAULT_CONFIG_PATHS: Vec> = vec![ + dirs_next::config_dir().map(|p| p.join("systeroid").join(DEFAULT_CONFIG)), + dirs_next::home_dir().map(|p| p.join(".systeroid").join(DEFAULT_CONFIG)), + ]; +} + +/// Macro for the concise initialization of HashMap macro_rules! map { ($( $key: expr => $val: expr ),*) => {{ let mut map = ::std::collections::HashMap::new(); @@ -12,9 +29,31 @@ macro_rules! map { }} } +/// Macro for parsing a boolean value from INI format +macro_rules! parse_ini_flag { + ($self: ident, $config: ident, $section: ident, $name: ident) => { + if let Some($name) = $section.get(stringify!($name)) { + $self.$config.$name = $name == "true"; + } + }; +} + /// Configuration. #[derive(Clone, Debug)] pub struct Config { + /// Whether if the deprecated variables should be included while listing. + pub display_deprecated: bool, + /// Path of the Linux kernel documentation. + pub kernel_docs: Option, + /// CLI configuration. + pub cli: CliConfig, + /// TUI configuration. + pub tui: TuiConfig, +} + +/// CLI configuration. +#[derive(Clone, Debug)] +pub struct CliConfig { /// Whether if the verbose logging is enabled. pub verbose: bool, /// Whether if the errors should be ignored. @@ -23,33 +62,196 @@ pub struct Config { pub quiet: bool, /// Whether if the pager is disabled. pub no_pager: bool, - /// Sections and the corresponding colors. - pub section_colors: HashMap, - /// Default color for the output - pub default_color: Color, /// Display type of the kernel parameters. pub display_type: DisplayType, + /// Output type of the application. + pub output_type: OutputType, + /// Color configuration. + pub color: CliColorConfig, +} + +/// CLI color configuration. +#[derive(Clone, Debug)] +pub struct CliColorConfig { + /// Default color for the output + pub default_color: Color, + /// Sections and the corresponding colors. + pub section_colors: HashMap, +} + +/// TUI configuration. +#[derive(Clone, Debug)] +pub struct TuiConfig { + /// Refresh rate of the terminal. + pub tick_rate: u64, + /// Do not parse/show Linux kernel documentation. + pub no_docs: bool, + /// Color configuration. + pub color: TuiColorConfig, +} + +/// TUI color configuration. +#[derive(Clone, Debug)] +pub struct TuiColorConfig { + /// Foreground color. + pub fg_color: String, + /// Background color. + pub bg_color: String, +} + +impl Config { + /// Parses the configuration file and overrides values. + pub fn parse(&mut self, path: Option) -> Result<()> { + let mut config_paths = DEFAULT_CONFIG_PATHS.clone(); + if path.is_some() { + config_paths.insert(0, path); + } + let mut config_path = None; + for path in config_paths.into_iter().flatten() { + if path.exists() { + config_path = Some(path); + break; + } + } + if let Some(path) = config_path { + let ini = Ini::load_from_file(path)?; + if let Some(general_section) = ini.section(Some("general")) { + if let Some(display_deprecated) = general_section.get("display_deprecated") { + self.display_deprecated = display_deprecated == "true"; + } + if let Some(kernel_docs) = general_section.get("kernel_docs") { + self.kernel_docs = Some(PathBuf::from(kernel_docs)); + } + } + if let Some(section) = ini.section(Some("cli")) { + parse_ini_flag!(self, cli, section, verbose); + parse_ini_flag!(self, cli, section, ignore_errors); + parse_ini_flag!(self, cli, section, quiet); + parse_ini_flag!(self, cli, section, no_pager); + if let Some(display_type) = section.get("display_type").map(DisplayType::from) { + self.cli.display_type = display_type; + } + if let Some(output_type) = section.get("output_type").map(OutputType::from) { + self.cli.output_type = output_type; + } + } + if let Some(section) = ini.section(Some("cli.colors")) { + if let Some(default_color) = section + .get("default_color") + .and_then(|v| Color::try_from(v).ok()) + { + self.cli.color.default_color = default_color; + } + for (key, value) in section.iter() { + if key.starts_with("section_") { + if let (sysctl_section, Some(color)) = ( + Section::from(key.trim_start_matches("section_").to_string()), + Color::try_from(value).ok(), + ) { + self.cli.color.section_colors.insert(sysctl_section, color); + } + } + } + } + if let Some(section) = ini.section(Some("tui")) { + if let Some(tick_rate) = section.get("tick_rate").and_then(|v| v.parse().ok()) { + self.tui.tick_rate = tick_rate; + } + parse_ini_flag!(self, tui, section, no_docs); + } + if let Some(section) = ini.section(Some("tui.colors")) { + if let Some(fg_color) = section.get("fg_color") { + self.tui.color.fg_color = fg_color.to_string(); + } + if let Some(bg_color) = section.get("bg_color") { + self.tui.color.bg_color = bg_color.to_string(); + } + } + } + Ok(()) + } } impl Default for Config { fn default() -> Self { Self { - verbose: false, - ignore_errors: false, - quiet: false, - no_pager: false, - section_colors: map! { - Section::Abi => Color::Red, - Section::Fs => Color::Green, - Section::Kernel => Color::Magenta, - Section::Net => Color::Blue, - Section::Sunrpc => Color::Yellow, - Section::User => Color::Cyan, - Section::Vm => Color::BrightRed, - Section::Unknown => Color::White + display_deprecated: false, + kernel_docs: None, + cli: CliConfig { + verbose: false, + ignore_errors: false, + quiet: false, + no_pager: false, + display_type: DisplayType::Default, + output_type: OutputType::Default, + color: CliColorConfig { + default_color: Color::BrightBlack, + section_colors: map! { + Section::Abi => Color::Red, + Section::Fs => Color::Green, + Section::Kernel => Color::Magenta, + Section::Net => Color::Blue, + Section::Sunrpc => Color::Yellow, + Section::User => Color::Cyan, + Section::Vm => Color::BrightRed, + Section::Unknown => Color::White + }, + }, + }, + tui: TuiConfig { + tick_rate: 250, + no_docs: false, + color: TuiColorConfig { + fg_color: String::from("white"), + bg_color: String::from("black"), + }, }, - default_color: Color::BrightBlack, - display_type: DisplayType::default(), } } } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_config() -> Result<()> { + let mut config = Config { + display_deprecated: true, + ..Default::default() + }; + config.cli.display_type = DisplayType::Value; + config.cli.color.default_color = Color::Blue; + config.cli.color.section_colors = HashMap::new(); + config.tui.tick_rate = 3000; + config.tui.color.fg_color = String::new(); + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("parent directory not found") + .join("config") + .join(DEFAULT_CONFIG); + config.parse(Some(path))?; + assert_eq!( + Config::default().display_deprecated, + config.display_deprecated + ); + assert_eq!( + Some(PathBuf::from("/usr/share/doc/linux")), + config.kernel_docs + ); + assert_eq!(Config::default().cli.display_type, config.cli.display_type); + assert_eq!( + Config::default().cli.color.default_color, + config.cli.color.default_color + ); + assert_eq!( + Config::default().cli.color.section_colors, + config.cli.color.section_colors + ); + assert_eq!(Config::default().tui.tick_rate, config.tui.tick_rate); + assert_eq!( + Config::default().tui.color.fg_color, + config.tui.color.fg_color + ); + Ok(()) + } +} diff --git a/systeroid-core/src/error.rs b/systeroid-core/src/error.rs index 9d3f5ed..524865d 100644 --- a/systeroid-core/src/error.rs +++ b/systeroid-core/src/error.rs @@ -24,6 +24,9 @@ pub enum Error { /// Error that may occur while handling sysctl operations. #[error("sysctl error: `{0}`")] SysctlError(#[from] sysctl::SysctlError), + /// Error that may occur while parsing an INI document. + #[error("INI parsing error: `{0}`")] + IniError(#[from] ini::Error), } /// Type alias for the standard [`Result`] type. diff --git a/systeroid-core/src/sysctl/controller.rs b/systeroid-core/src/sysctl/controller.rs index 700176b..6df4587 100644 --- a/systeroid-core/src/sysctl/controller.rs +++ b/systeroid-core/src/sysctl/controller.rs @@ -4,12 +4,13 @@ use crate::error::Result; use crate::parsers::{parse_kernel_docs, KERNEL_DOCS_PATH}; use crate::sysctl::parameter::Parameter; use crate::sysctl::section::Section; +use crate::sysctl::DEPRECATED_PARAMS; use crate::sysctl::{DISABLE_CACHE_ENV, PARAMETERS_CACHE_LABEL, PROC_PATH}; use parseit::globwalk; use rayon::prelude::*; use std::convert::TryFrom; use std::env; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::result::Result as StdResult; use sysctl::{CtlFlags, CtlIter, Sysctl as SysctlImpl}; @@ -33,10 +34,20 @@ impl Sysctl { }) { match Parameter::try_from(&ctl) { Ok(parameter) => { - parameters.push(parameter); + if !config.display_deprecated { + let mut skip_param = false; + if let Some(param_name) = parameter.get_absolute_name() { + skip_param = DEPRECATED_PARAMS.contains(¶m_name); + } + if !skip_param { + parameters.push(parameter); + } + } else { + parameters.push(parameter); + } } Err(e) => { - if config.verbose { + if config.cli.verbose { eprintln!("{} ({})", e, ctl.name()?); } } @@ -48,21 +59,22 @@ impl Sysctl { /// Returns the first found parameter in the available parameters. #[cfg(test)] fn get_parameter(&self, query: &str) -> Option<&Parameter> { - self.get_parameters(query).first().map(|v| *v) + self.get_parameters(query).first().copied() } /// Returns the parameters that matches the given query. pub fn get_parameters(&self, query: &str) -> Vec<&Parameter> { + let query = query.replace('/', "."); let parameters = self .parameters .iter() .filter(|param| { - param.name == query.replace('/', ".") - || param.section.to_string() == query - || param.get_absolute_name() == Some(&query.replace('/', ".")) + param.name == query + || param.get_absolute_name() == Some(&query) + || param.is_in_section(&query) }) .collect::>(); - if parameters.is_empty() && !self.config.ignore_errors { + if parameters.is_empty() && !self.config.cli.ignore_errors { eprintln!( "{}: cannot stat {}{}: No such file or directory", env!("CARGO_PKG_NAME").split('-').collect::>()[0], @@ -74,12 +86,8 @@ impl Sysctl { } /// Updates the descriptions of the kernel parameters using the given cached data. - pub fn update_docs_from_cache( - &mut self, - kernel_docs: Option<&PathBuf>, - cache: &Cache, - ) -> Result<()> { - let mut kernel_docs_path = if let Some(path) = kernel_docs { + pub fn update_docs_from_cache(&mut self, cache: &Cache) -> Result<()> { + let mut kernel_docs_path = if let Some(path) = &self.config.kernel_docs { vec![path.to_path_buf()] } else { Vec::new() @@ -119,17 +127,17 @@ impl Sysctl { /// Updates the parameters internally using the given list. /// /// Keeps the original values. - fn update_params(&mut self, mut parameters: Vec) { - parameters.par_iter_mut().for_each(|parameter| { - if let Some(param) = self - .parameters + fn update_params(&mut self, parameters: Vec) { + self.parameters.par_iter_mut().for_each(|parameter| { + if let Some(param) = parameters .par_iter() .find_any(|param| param.name == parameter.name) { - parameter.value = param.value.to_string(); + parameter.description = param.description.clone(); + parameter.docs_path = param.docs_path.clone(); + parameter.docs_title = param.docs_title.clone(); } }); - self.parameters = parameters; } /// Updates the descriptions of the kernel parameters. @@ -178,21 +186,27 @@ mod tests { assert!(sysctl.get_parameter("kernel.hostname").is_some()); assert!(sysctl.get_parameter("unexisting.param").is_none()); assert_eq!( - "Linux", - sysctl.get_parameters("ostype").first().unwrap().value + Some(String::from("Linux")), + sysctl + .get_parameters("ostype") + .first() + .map(|v| v.value.to_string()) ); assert!(sysctl.get_parameters("---").is_empty()); - sysctl.update_docs_from_cache(None, &Cache::init()?)?; + sysctl.update_docs_from_cache(&Cache::init()?)?; - let parameter = sysctl.get_parameter("kernel.hostname").unwrap().clone(); + let parameter = sysctl + .get_parameter("kernel.hostname") + .expect("failed to get parameter") + .clone(); let old_value = parameter.docs_title; let parameters = sysctl.parameters.clone(); sysctl .parameters .iter_mut() .find(|param| param.name == parameter.name) - .unwrap() + .expect("parameter not found") .docs_title = String::from("-"); sysctl.update_params(parameters); assert_eq!( @@ -201,24 +215,24 @@ mod tests { .parameters .iter_mut() .find(|param| param.name == parameter.name) - .unwrap() + .expect("parameter not found") .docs_title ); assert!(sysctl .get_parameter("vm.zone_reclaim_mode") - .unwrap() + .expect("failed to get parameter") .description .as_ref() - .unwrap() + .expect("parameter has no description") .contains("zone_reclaim_mode is disabled by default.")); assert!(sysctl .get_parameter("user.max_user_namespaces") - .unwrap() + .expect("failed to get parameter") .description .as_ref() - .unwrap() + .expect("parameter has no description") .contains("The maximum number of user namespaces")); Ok(()) diff --git a/systeroid-core/src/sysctl/display.rs b/systeroid-core/src/sysctl/display.rs deleted file mode 100644 index a408550..0000000 --- a/systeroid-core/src/sysctl/display.rs +++ /dev/null @@ -1,18 +0,0 @@ -/// Possible ways of displaying the kernel parameters. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum DisplayType { - /// Print the kernel parameter name along with its value. - Default, - /// Print only the name of the parameter. - Name, - /// Print only the value of the parameter. - Value, - /// Print only the value of the parameter without new line. - Binary, -} - -impl Default for DisplayType { - fn default() -> Self { - Self::Default - } -} diff --git a/systeroid-core/src/sysctl/mod.rs b/systeroid-core/src/sysctl/mod.rs index 394e756..04a3339 100644 --- a/systeroid-core/src/sysctl/mod.rs +++ b/systeroid-core/src/sysctl/mod.rs @@ -4,8 +4,8 @@ pub mod controller; /// Sysctl section. pub mod section; -/// Sysctl display options. -pub mod display; +/// Sysctl display/output options. +pub mod r#type; /// Kernel parameter. pub mod parameter; diff --git a/systeroid-core/src/sysctl/parameter.rs b/systeroid-core/src/sysctl/parameter.rs index d89103c..0ad7fbc 100644 --- a/systeroid-core/src/sysctl/parameter.rs +++ b/systeroid-core/src/sysctl/parameter.rs @@ -1,9 +1,10 @@ use crate::config::Config; use crate::error::Result; -use crate::sysctl::display::DisplayType; +use crate::sysctl::r#type::DisplayType; use crate::sysctl::section::Section; use colored::*; use serde::{Deserialize, Serialize}; +use std::fmt::Write as _; use std::io::Write; use std::path::PathBuf; use sysctl::{Ctl, Sysctl as SysctlImpl}; @@ -52,19 +53,22 @@ impl Parameter { /// Returns the parameter name with corresponding section colors. pub fn get_colored_name(&self, config: &Config) -> String { let section_color = *(config + .cli + .color .section_colors .get(&self.section) - .unwrap_or(&config.default_color)); + .unwrap_or(&config.cli.color.default_color)); let fields = self.name.split('.').collect::>(); fields .iter() .enumerate() .fold(String::new(), |mut result, (i, v)| { if i != fields.len() - 1 { - result += &format!( + let _ = write!( + result, "{}{}", v.color(section_color), - ".".color(config.default_color) + ".".color(config.cli.color.default_color) ); } else { result += v; @@ -73,14 +77,16 @@ impl Parameter { }) } - /// Returns the components of the parameter to contruct a [`Tree`]. + /// Returns the components of the parameter to construct a [`Tree`]. /// /// [`Tree`]: crate::tree::Tree pub fn get_tree_components(&self, config: &Config) -> Vec { let section_color = *(config + .cli + .color .section_colors .get(&self.section) - .unwrap_or(&config.default_color)); + .unwrap_or(&config.cli.color.default_color)); let mut components = self .name .split('.') @@ -93,11 +99,11 @@ impl Parameter { .for_each(|(i, component)| { if i != total_components - 1 { *component = component.color(section_color).to_string(); - } else if config.display_type != DisplayType::Name { + } else if config.cli.display_type != DisplayType::Name { *component = format!( "{} {} {}", component, - "=".color(config.default_color), + "=".color(config.cli.color.default_color), self.value.replace('\n', " ").bold() ); } @@ -107,7 +113,7 @@ impl Parameter { /// Prints the kernel parameter to given output. pub fn display_value(&self, config: &Config, output: &mut Output) -> Result<()> { - match config.display_type { + match config.cli.display_type { DisplayType::Name => { writeln!(output, "{}", self.get_colored_name(config))?; } @@ -123,7 +129,7 @@ impl Parameter { output, "{} {} {}", self.get_colored_name(config), - "=".color(config.default_color), + "=".color(config.cli.color.default_color), value.bold(), )?; } @@ -185,11 +191,27 @@ impl Parameter { let ctl = Ctl::new(&self.name)?; let new_value = ctl.set_value_string(new_value)?; self.value = new_value; - if !config.quiet { + if !config.cli.quiet { self.display_value(config, output)?; } Ok(()) } + + /// Performs a search for given query and returns true if + /// the parameter is in the given sub/section. + pub fn is_in_section(&self, query: &str) -> bool { + let mut subsection = self.section.to_string(); + let mut components = self.name.split('.').skip(1).peekable(); + while let Some(component) = components.next() { + if query == subsection { + return true; + } + if components.peek().is_some() { + subsection = format!("{}.{}", subsection, component) + } + } + false + } } #[cfg(test)] @@ -208,9 +230,16 @@ mod tests { }; assert_eq!(Some("test_param"), parameter.get_absolute_name()); - let mut config = Config::default(); - config.default_color = Color::White; - *(config.section_colors.get_mut(&Section::Kernel).unwrap()) = Color::Yellow; + let mut config = Config { + ..Default::default() + }; + config.cli.color.default_color = Color::White; + *(config + .cli + .color + .section_colors + .get_mut(&Section::Kernel) + .expect("failed to get color")) = Color::Yellow; assert_eq!(parameter.name, parameter.get_colored_name(&config)); assert_eq!( @@ -230,7 +259,7 @@ mod tests { ); output.clear(); - config.display_type = DisplayType::Name; + config.cli.display_type = DisplayType::Name; parameter.display_value(&config, &mut output)?; assert_eq!( "kernel.fictional.test_param\n", @@ -238,12 +267,12 @@ mod tests { ); output.clear(); - config.display_type = DisplayType::Value; + config.cli.display_type = DisplayType::Value; parameter.display_value(&config, &mut output)?; assert_eq!("1\n", String::from_utf8_lossy(&output)); output.clear(); - config.display_type = DisplayType::Binary; + config.cli.display_type = DisplayType::Binary; parameter.display_value(&config, &mut output)?; assert_eq!("1", String::from_utf8_lossy(&output)); @@ -275,6 +304,19 @@ mod tests { .update_value("0", &config, &mut Vec::new()) .is_err()); + parameter.name = String::from("kernel.fictional.testing.xyz.parameter"); + assert!(parameter.is_in_section(¶meter.section.to_string())); + assert!(parameter.is_in_section("kernel")); + assert!(parameter.is_in_section("kernel.fictional")); + assert!(parameter.is_in_section("kernel.fictional.testing")); + assert!(parameter.is_in_section("kernel.fictional.testing.xyz")); + assert!(!parameter.is_in_section("xyz")); + assert!(!parameter.is_in_section("test")); + assert!(!parameter.is_in_section("ker")); + assert!(!parameter.is_in_section("kernel.fi")); + assert!(!parameter.is_in_section("kernel.fictional.tes")); + assert!(!parameter.is_in_section(¶meter.name)); + Ok(()) } } diff --git a/systeroid-core/src/sysctl/type.rs b/systeroid-core/src/sysctl/type.rs new file mode 100644 index 0000000..ee70c44 --- /dev/null +++ b/systeroid-core/src/sysctl/type.rs @@ -0,0 +1,73 @@ +/// Macro for generating enum type with a Default variant and common implementations. +macro_rules! gen_type_property { + ($name: ident, + $($variant: ident,)+ + ) => { + /// Enum containing variants. + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub enum $name { + /// Default variant. + Default, + $( + /// Variant. + $variant + ),+ + } + + impl<'a> From<&'a str> for $name { + fn from(value: &'a str) -> Self { + for section in Self::variants() { + if value.to_lowercase() == section.to_string() { + return *section; + } + } + Self::Default + } + } + + impl Default for $name { + fn default() -> Self { + Self::Default + } + } + + impl ::std::fmt::Display for $name { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", format!("{:?}", self).to_lowercase()) + } + } + + impl $name { + /// Returns the variants. + pub fn variants() -> &'static [Self] { + &[Self::Default, $(Self::$variant),+] + } + } + }; +} + +gen_type_property!(DisplayType, Name, Value, Binary,); +gen_type_property!(OutputType, Tree, Json,); + +#[cfg(test)] +mod tests { + #[test] + fn test_gen_type() { + gen_type_property!(TestType, One, Two, Three,); + assert_eq!(TestType::Two, TestType::from("two")); + assert_eq!(TestType::Two, TestType::from("TwO")); + assert_eq!(TestType::Default, TestType::from("tw0")); + assert_eq!(TestType::Default, TestType::default()); + assert_eq!("three", &TestType::Three.to_string()); + assert_eq!("one", &TestType::One.to_string()); + assert_eq!( + &[ + TestType::Default, + TestType::One, + TestType::Two, + TestType::Three + ], + TestType::variants() + ); + } +} diff --git a/systeroid-core/src/tree.rs b/systeroid-core/src/tree.rs index 8982552..cc745c2 100644 --- a/systeroid-core/src/tree.rs +++ b/systeroid-core/src/tree.rs @@ -220,12 +220,12 @@ mod tests { } #[test] - fn test_tree_output() { + fn test_tree_output() -> IoResult<()> { let lines = ["a", "a/b/e", "a/b", "a/b/c/d"]; let tree = Tree::from_input(&mut lines.iter(), '/'); let mut output = Vec::new(); - tree.print(&mut output, Color::White).unwrap(); + tree.print(&mut output, Color::White)?; let expected_output = "\ a @@ -235,10 +235,12 @@ a └── d\n"; assert_eq!(expected_output, String::from_utf8_lossy(&output)); + + Ok(()) } #[test] - fn test_print_line() { + fn test_print_line() -> IoResult<()> { let value = String::from("abc\ndef"); let mut output = Vec::new(); @@ -246,8 +248,7 @@ a value: value.to_string(), childs: Vec::new(), } - .print_line(&mut output, &[], Color::White) - .unwrap(); + .print_line(&mut output, &[], Color::White)?; assert_eq!(b"abc\ndef\n", &*output); let mut output = Vec::new(); @@ -255,17 +256,17 @@ a value: value.to_string(), childs: Vec::new(), } - .print_line(&mut output, &[true, false, true], Color::White) - .unwrap(); + .print_line(&mut output, &[true, false, true], Color::White)?; assert_eq!(" │ └── abc\ndef\n".as_bytes(), &*output); let mut output = Vec::new(); TreeNode { - value: value.to_string(), + value, childs: Vec::new(), } - .print_line(&mut output, &[true, false, false], Color::White) - .unwrap(); + .print_line(&mut output, &[true, false, false], Color::White)?; assert_eq!(" │ ├── abc\ndef\n".as_bytes(), &*output); + + Ok(()) } } diff --git a/systeroid-tui/Cargo.toml b/systeroid-tui/Cargo.toml index e8101e3..17f6ea0 100644 --- a/systeroid-tui/Cargo.toml +++ b/systeroid-tui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "systeroid-tui" -version = "0.1.1" # managed by release.sh +version = "0.2.1" # managed by release.sh description = "A terminal user interface for managing kernel parameters" authors = ["Orhun Parmaksız "] license = "MIT OR Apache-2.0" @@ -10,7 +10,7 @@ repository = "https://github.com/orhun/systeroid" keywords = ["linux", "kernel", "parameter", "sysctl", "tui"] categories = ["command-line-utilities"] edition = "2021" -rust-version = "1.56.1" +rust-version = "1.57.0" [features] # clipboard support is enabled as default @@ -20,16 +20,22 @@ clipboard = ["copypasta-ext"] [dependencies] termion = "1.5.6" unicode-width = "0.1.9" -thiserror = "1.0.30" +thiserror = "1.0.34" getopts = "0.2.21" copypasta-ext = { version = "0.3.7", optional = true } -colorsys = "0.6.5" +colorsys = "0.6.6" [dependencies.systeroid-core] -version = "0.1.1" # managed by release.sh +version = "0.2.1" # managed by release.sh path = "../systeroid-core" [dependencies.tui] -version = "0.18.0" +version = "0.19.0" default-features = false features = ["termion"] + +# metadata for cargo-binstall to get the right artifacts +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/systeroid-{ version }-{ target }.{ archive-format }" +bin-dir = "systeroid-{ version }/{ bin }{ binary-ext }" +pkg-fmt = "tgz" diff --git a/systeroid-tui/src/args.rs b/systeroid-tui/src/args.rs index 4ca367d..43341ed 100644 --- a/systeroid-tui/src/args.rs +++ b/systeroid-tui/src/args.rs @@ -1,7 +1,7 @@ -use crate::style::Colors; use getopts::Options; use std::env; use std::path::PathBuf; +use systeroid_core::config::CONFIG_ENV; use systeroid_core::sysctl::section::Section; use systeroid_core::sysctl::KERNEL_DOCS_ENV; @@ -18,6 +18,8 @@ For more details see {bin}(8)."#; /// Command-line arguments. #[derive(Debug, Default)] pub struct Args { + /// Location of the configuration file. + pub config: Option, /// Refresh rate of the terminal. pub tick_rate: u64, /// Path of the Linux kernel documentation. @@ -26,10 +28,14 @@ pub struct Args { pub section: Option
, /// Query to search on startup. pub search_query: Option, - /// Background/foreground colors. - pub colors: Colors, + /// Foreground color. + pub fg_color: String, + /// Background color. + pub bg_color: String, /// Do not parse/show Linux kernel documentation. pub no_docs: bool, + /// Whether if the deprecated variables should be included while listing. + pub display_deprecated: bool, } impl Args { @@ -63,6 +69,17 @@ impl Args { "", ); opts.optflag("n", "no-docs", "do not show the kernel documentation"); + opts.optflag( + "", + "deprecated", + "include deprecated variables while listing", + ); + opts.optopt( + "c", + "config", + "set the path of the configuration file", + "", + ); opts.optflag("h", "help", "display this help and exit"); opts.optflag("V", "version", "output version information and exit"); opts @@ -99,13 +116,18 @@ impl Args { .map(PathBuf::from), section: matches.opt_str("s").map(Section::from), search_query: matches.opt_str("q"), - colors: Colors::new( - matches.opt_str("bg-color").as_deref().unwrap_or("black"), - matches.opt_str("fg-color").as_deref().unwrap_or("white"), - ) - .map_err(|e| eprintln!("error: `{}`", e)) - .ok()?, + fg_color: matches + .opt_str("fg-color") + .unwrap_or_else(|| String::from("white")), + bg_color: matches + .opt_str("bg-color") + .unwrap_or_else(|| String::from("black")), no_docs: matches.opt_present("n"), + display_deprecated: matches.opt_present("deprecated"), + config: matches + .opt_str("c") + .or_else(|| env::var(CONFIG_ENV).ok()) + .map(PathBuf::from), }) } } @@ -136,7 +158,7 @@ mod tests { String::from("-q"), String::from("test"), ]) - .unwrap(); + .expect("failed to parse arguments"); assert_eq!(1000, args.tick_rate); assert_eq!(Some(PathBuf::from("/docs")), args.kernel_docs); diff --git a/systeroid-tui/src/command.rs b/systeroid-tui/src/command.rs index 8280a70..28e1b3e 100644 --- a/systeroid-tui/src/command.rs +++ b/systeroid-tui/src/command.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use termion::event::Key; /// Possible application commands. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub enum Command { /// Show help. Help, @@ -47,17 +47,14 @@ impl FromStr for Command { "exit" | "quit" | "q" | "q!" => Ok(Command::Exit), _ => { if s.starts_with("set") { - let values: Vec<&str> = s - .trim_start_matches("set") - .trim() - .split_whitespace() - .collect(); + let values: Vec<&str> = + s.trim_start_matches("set").split_whitespace().collect(); Ok(Command::Set( values.first().ok_or(())?.to_string(), values[1..].join(" "), )) } else if s.starts_with("scroll") { - let mut values = s.trim_start_matches("scroll").trim().split_whitespace(); + let mut values = s.trim_start_matches("scroll").split_whitespace(); Ok(Command::Scroll( ScrollArea::try_from(values.next().ok_or(())?)?, Direction::try_from(values.next().ok_or(())?)?, @@ -157,7 +154,7 @@ mod tests { "scroll list bottom 1", ), ] { - assert_eq!(command, Command::from_str(value).unwrap()); + assert_eq!(Ok(command), Command::from_str(value)); } assert!(Command::from_str("---").is_err()); assert_command_parser! { diff --git a/systeroid-tui/src/event.rs b/systeroid-tui/src/event.rs index b3fec84..ff71984 100644 --- a/systeroid-tui/src/event.rs +++ b/systeroid-tui/src/event.rs @@ -58,13 +58,14 @@ impl EventHandler { #[cfg(test)] mod tests { use super::*; + use crate::error::Result; use std::char; use std::time::Instant; const TICK_RATE_MS: u64 = 100; #[test] - fn test_event() { + fn test_event() -> Result<()> { let start_time = Instant::now(); let event_handler = EventHandler::new(TICK_RATE_MS); let mut tick_count = 0; @@ -73,9 +74,9 @@ mod tests { thread::spawn(move || { let key = Key::Char(char::from_digit(i, 10).unwrap_or('9')); let event = Event::KeyPress(key); - sender.send(event).unwrap(); + sender.send(event).expect("failed to send event"); }); - match event_handler.next().unwrap() { + match event_handler.next()? { Event::KeyPress(key) => { if key == Key::Char('9') { break; @@ -88,5 +89,6 @@ mod tests { } } assert!(start_time.elapsed() > Duration::from_millis(tick_count * TICK_RATE_MS)); + Ok(()) } } diff --git a/systeroid-tui/src/lib.rs b/systeroid-tui/src/lib.rs index 5b2a98a..3ea6aa4 100644 --- a/systeroid-tui/src/lib.rs +++ b/systeroid-tui/src/lib.rs @@ -26,6 +26,7 @@ use crate::args::Args; use crate::command::Command; use crate::error::Result; use crate::event::{Event, EventHandler}; +use crate::style::Colors; use systeroid_core::cache::Cache; use systeroid_core::config::Config; use systeroid_core::sysctl::controller::Sysctl; @@ -34,14 +35,25 @@ use tui::terminal::Terminal; /// Runs `systeroid-tui`. pub fn run(args: Args, backend: B) -> Result<()> { - let mut sysctl = Sysctl::init(Config::default())?; - if !args.no_docs { - sysctl.update_docs_from_cache(args.kernel_docs.as_ref(), &Cache::init()?)?; + let mut config = Config { + display_deprecated: args.display_deprecated, + kernel_docs: args.kernel_docs, + ..Default::default() + }; + config.tui.tick_rate = args.tick_rate; + config.tui.no_docs = args.no_docs; + config.tui.color.fg_color = args.fg_color; + config.tui.color.bg_color = args.bg_color; + config.parse(args.config)?; + let colors = Colors::new(&config.tui.color.bg_color, &config.tui.color.fg_color)?; + let mut sysctl = Sysctl::init(config)?; + if !sysctl.config.tui.no_docs { + sysctl.update_docs_from_cache(&Cache::init()?)?; } let mut terminal = Terminal::new(backend)?; terminal.hide_cursor()?; terminal.clear()?; - let event_handler = EventHandler::new(args.tick_rate); + let event_handler = EventHandler::new(sysctl.config.tui.tick_rate); let mut app = App::new(&mut sysctl); if let Some(section) = args.section { app.section_list.state.select(Some( @@ -59,7 +71,7 @@ pub fn run(args: Args, backend: B) -> Result<()> { app.input = None; } while app.running { - terminal.draw(|frame| ui::render(frame, &mut app, &args.colors))?; + terminal.draw(|frame| ui::render(frame, &mut app, &colors))?; match event_handler.next()? { Event::KeyPress(key) => { let command = Command::parse(key, app.is_input_mode()); @@ -87,6 +99,8 @@ mod tests { fn test_systeroid_tui() -> Result<()> { let args = Args { tick_rate: 1000, + fg_color: String::from("white"), + bg_color: String::from("black"), ..Args::default() }; let backend = TestBackend::new(40, 10); diff --git a/systeroid-tui/src/options.rs b/systeroid-tui/src/options.rs index 4377dc7..ad8d5d5 100644 --- a/systeroid-tui/src/options.rs +++ b/systeroid-tui/src/options.rs @@ -5,7 +5,7 @@ macro_rules! generate_option { $($variant: ident => $str_repr: expr,)+ ) => { /// Available options. - #[derive(Clone, Copy, Debug, PartialEq)] + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum $name { $( /// Option. diff --git a/systeroid-tui/src/style.rs b/systeroid-tui/src/style.rs index 99f7c14..9ad07c4 100644 --- a/systeroid-tui/src/style.rs +++ b/systeroid-tui/src/style.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use tui::style::{Color as TuiColor, Style}; /// Color configuration. -#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct Colors { /// Background color. bg: Color, @@ -39,7 +39,7 @@ impl Colors { } /// Wrapper for widget colors. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Color { /// Inner type. inner: TuiColor, @@ -76,13 +76,13 @@ impl FromStr for Color { "magenta" => TuiColor::Magenta, "cyan" => TuiColor::Cyan, "gray" => TuiColor::Gray, - "darkgray" => TuiColor::DarkGray, - "lightred" => TuiColor::LightRed, - "lightgreen" => TuiColor::LightGreen, - "lightyellow" => TuiColor::LightYellow, - "lightblue" => TuiColor::LightBlue, - "lightmagenta" => TuiColor::LightMagenta, - "lightcyan" => TuiColor::LightCyan, + "darkgray" | "dark gray" => TuiColor::DarkGray, + "lightred" | "light red" => TuiColor::LightRed, + "lightgreen" | "light green" => TuiColor::LightGreen, + "lightyellow" | "light yellow" => TuiColor::LightYellow, + "lightblue" | "light blue" => TuiColor::LightBlue, + "lightmagenta" | "light magenta" => TuiColor::LightMagenta, + "lightcyan" | "light cyan" => TuiColor::LightCyan, "white" => TuiColor::White, _ => { let rgb = Rgb::from_hex_str(&format!("#{}", s))?; @@ -102,6 +102,8 @@ mod tests { assert_eq!(TuiColor::Gray, Color::from_str("gray")?.get()); assert_eq!(TuiColor::Black, Color::from_str("black")?.get()); assert_eq!(TuiColor::Green, Color::from_str("green")?.get()); + assert_eq!(TuiColor::LightRed, Color::from_str("light red")?.get()); + assert_eq!(TuiColor::LightBlue, Color::from_str("lightblue")?.get()); assert_eq!( TuiColor::Rgb(152, 157, 69), Color::from_str("989D45")?.get() diff --git a/systeroid/Cargo.toml b/systeroid/Cargo.toml index fd1a017..dba98e6 100644 --- a/systeroid/Cargo.toml +++ b/systeroid/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "systeroid" -version = "0.1.1" # managed by release.sh +version = "0.2.1" # managed by release.sh description = "A more powerful alternative to sysctl" authors = ["Orhun Parmaksız "] license = "MIT OR Apache-2.0" @@ -21,5 +21,11 @@ live-tests = [] getopts = "0.2.21" [dependencies.systeroid-core] -version = "0.1.1" # managed by release.sh +version = "0.2.1" # managed by release.sh path = "../systeroid-core" + +# metadata for cargo-binstall to get the right artifacts +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ version }-{ target }.{ archive-format }" +bin-dir = "{ name }-{ version }/{ bin }{ binary-ext }" +pkg-fmt = "tgz" diff --git a/systeroid/src/app.rs b/systeroid/src/app.rs index cd2ca1f..7d6475a 100644 --- a/systeroid/src/app.rs +++ b/systeroid/src/app.rs @@ -1,4 +1,3 @@ -use crate::output::OutputType; use std::env; use std::io::{self, BufRead, Write}; use std::path::PathBuf; @@ -9,6 +8,7 @@ use systeroid_core::parseit::reader; use systeroid_core::parseit::regex::Regex; use systeroid_core::sysctl::controller::Sysctl; use systeroid_core::sysctl::parameter::Parameter; +use systeroid_core::sysctl::r#type::OutputType; use systeroid_core::sysctl::{DEPRECATED_PARAMS, SYSTEM_PRELOAD}; use systeroid_core::tree::{Tree, TreeNode}; @@ -19,18 +19,12 @@ pub struct App<'a, Output: Write> { sysctl: &'a mut Sysctl, /// Standard output. output: &'a mut Output, - /// Output type. - output_type: OutputType, } impl<'a, Output: Write> App<'a, Output> { /// Constructs a new instance. - pub fn new(sysctl: &'a mut Sysctl, output: &'a mut Output, output_type: OutputType) -> Self { - Self { - sysctl, - output, - output_type, - } + pub fn new(sysctl: &'a mut Sysctl, output: &'a mut Output) -> Self { + Self { sysctl, output } } /// Prints the given parameters to stdout. @@ -38,7 +32,7 @@ impl<'a, Output: Write> App<'a, Output> { where I: Iterator, { - match self.output_type { + match self.sysctl.config.cli.output_type { OutputType::Default => { parameters.try_for_each(|parameter| { parameter.display_value(&self.sysctl.config, self.output) @@ -54,7 +48,8 @@ impl<'a, Output: Write> App<'a, Output> { .map(|v| v.as_ref()), ); }); - Tree::new(root_node.childs).print(self.output, self.sysctl.config.default_color)?; + Tree::new(root_node.childs) + .print(self.output, self.sysctl.config.cli.color.default_color)?; } OutputType::Json => { Parameter::display_bulk_json(parameters.collect(), self.output)?; @@ -64,22 +59,12 @@ impl<'a, Output: Write> App<'a, Output> { } /// Displays all of the available kernel parameters. - pub fn display_parameters( - &mut self, - pattern: Option, - display_deprecated: bool, - explain: bool, - ) -> Result<()> { + pub fn display_parameters(&mut self, pattern: Option, explain: bool) -> Result<()> { let parameters = self.sysctl.parameters.clone(); let mut parameters = parameters.iter().filter(|parameter| { if let Some(pattern) = &pattern { return pattern.is_match(¶meter.name); } - if !display_deprecated { - if let Some(param_name) = parameter.get_absolute_name() { - return !DEPRECATED_PARAMS.contains(¶m_name); - } - } true }); if explain { @@ -91,7 +76,7 @@ impl<'a, Output: Write> App<'a, Output> { /// Displays the documentation of a parameter. pub fn display_documentation(&mut self, param_name: &str) -> Result<()> { - let no_pager = self.sysctl.config.no_pager; + let no_pager = self.sysctl.config.cli.no_pager; for parameter in self.sysctl.get_parameters(param_name) { let mut fallback_to_default = false; if no_pager { @@ -182,7 +167,9 @@ impl<'a, Output: Write> App<'a, Output> { /// Processes the parameters in the given file. pub fn preload_from_file(&mut self, path: PathBuf) -> Result<()> { if path == PathBuf::from("-") { - for line in io::stdin().lock().lines() { + let stdin = io::stdin(); + let lines = stdin.lock().lines(); + for line in lines { if let Err(e) = self.process_parameter(line?, true, false) { println!("{}: {}", env!("CARGO_PKG_NAME"), e); } @@ -239,22 +226,21 @@ mod tests { #[test] fn test_app() -> Result<()> { let mut output = Vec::new(); - let mut sysctl = Sysctl::init(Config { - no_pager: true, - ..Config::default() - })?; - sysctl.update_docs_from_cache(None, &Cache::init()?)?; + let mut config = Config::default(); + config.cli.no_pager = true; + let mut sysctl = Sysctl::init(config)?; + sysctl.update_docs_from_cache(&Cache::init()?)?; - let mut app = App::new(&mut sysctl, &mut output, OutputType::Default); + let mut app = App::new(&mut sysctl, &mut output); - app.display_parameters(Regex::new("kernel|vm").ok(), false, false)?; + app.display_parameters(Regex::new("kernel|vm").ok(), false)?; let result = String::from_utf8_lossy(app.output); assert!(result.contains("vm.zone_reclaim_mode =")); assert!(result.contains("kernel.version =")); app.output.clear(); - app.output_type = OutputType::Tree; - app.display_parameters(None, true, false)?; + app.sysctl.config.cli.output_type = OutputType::Tree; + app.display_parameters(None, false)?; assert!(String::from_utf8_lossy(app.output).contains("─ osrelease =")); app.output.clear(); @@ -263,7 +249,7 @@ mod tests { app.output.clear(); let param_name = String::from("kernel.version"); - app.output_type = OutputType::Default; + app.sysctl.config.cli.output_type = OutputType::Default; app.process_parameter(param_name.clone(), true, false)?; let result = String::from_utf8_lossy(app.output); assert_eq!(1, result.lines().count()); @@ -271,7 +257,7 @@ mod tests { app.output.clear(); let param_name = String::from("kernel.version"); - app.output_type = OutputType::Json; + app.sysctl.config.cli.output_type = OutputType::Json; app.process_parameter(param_name.clone(), true, false)?; let result = String::from_utf8_lossy(app.output); assert!(result.contains("\"section\":\"kernel\"")); diff --git a/systeroid/src/args.rs b/systeroid/src/args.rs index 0cc8819..57ddccb 100644 --- a/systeroid/src/args.rs +++ b/systeroid/src/args.rs @@ -1,9 +1,10 @@ -use crate::output::OutputType; use getopts::Options; use std::env; use std::path::PathBuf; +use systeroid_core::config::CONFIG_ENV; use systeroid_core::parseit::regex::Regex; -use systeroid_core::sysctl::display::DisplayType; +use systeroid_core::sysctl::r#type::DisplayType; +use systeroid_core::sysctl::r#type::OutputType; use systeroid_core::sysctl::{DEFAULT_PRELOAD, KERNEL_DOCS_ENV}; /// Help message for the arguments. @@ -19,6 +20,8 @@ For more details see {bin}(8)."#; /// Command-line arguments. #[derive(Debug, Default)] pub struct Args { + /// Location of the configuration file. + pub config: Option, /// Whether if the verbose logging is enabled. pub verbose: bool, /// Whether if the quiet mode is enabled. @@ -97,6 +100,12 @@ impl Args { opts.optflag("P", "no-pager", "do not pipe output into a pager"); opts.optflag("v", "verbose", "enable verbose logging"); opts.optflag("", "tui", "show terminal user interface"); + opts.optopt( + "c", + "config", + "set the path of the configuration file", + "", + ); opts.optflag("h", "help", "display this help and exit (-d)"); opts.optflag("V", "version", "output version information and exit"); opts @@ -195,6 +204,10 @@ impl Args { explain: matches.opt_present("E"), output_type, show_tui: matches.opt_present("tui"), + config: matches + .opt_str("c") + .or_else(|| env::var(CONFIG_ENV).ok()) + .map(PathBuf::from), values: matches.free, }) } @@ -222,20 +235,20 @@ mod tests { String::from("-A"), String::from("-T"), ]) - .unwrap(); + .expect("failed to parse arguments"); assert!(args.verbose); assert!(args.write); assert_eq!(OutputType::Tree, args.output_type); assert!(!Args::parse(vec![String::new(), String::from("-p")]) - .unwrap() + .expect("failed to parse arguments") .values .is_empty()); assert_eq!( DisplayType::Binary, Args::parse(vec![String::new(), String::from("-A"), String::from("-b")]) - .unwrap() + .expect("failed to parse arguments") .display_type ); } diff --git a/systeroid/src/lib.rs b/systeroid/src/lib.rs index 2790e51..fd6f220 100644 --- a/systeroid/src/lib.rs +++ b/systeroid/src/lib.rs @@ -6,8 +6,6 @@ pub mod app; /// Command-line argument parser. pub mod args; -/// Application output types. -pub mod output; use crate::app::App; use crate::args::Args; @@ -20,24 +18,28 @@ use systeroid_core::sysctl::controller::Sysctl; /// Runs `systeroid`. pub fn run(args: Args, output: &mut Output) -> Result<()> { - let config = Config { - verbose: args.verbose, - ignore_errors: args.ignore_errors, - quiet: args.quiet, - no_pager: args.no_pager, - display_type: args.display_type, + let mut config = Config { + display_deprecated: args.display_deprecated, + kernel_docs: args.kernel_docs, ..Default::default() }; + config.cli.verbose = args.verbose; + config.cli.ignore_errors = args.ignore_errors; + config.cli.quiet = args.quiet; + config.cli.no_pager = args.no_pager; + config.cli.display_type = args.display_type; + config.cli.output_type = args.output_type; + config.parse(args.config)?; let mut sysctl = Sysctl::init(config)?; if args.explain { - sysctl.update_docs_from_cache(args.kernel_docs.as_ref(), &Cache::init()?)?; + sysctl.update_docs_from_cache(&Cache::init()?)?; } - let mut app = App::new(&mut sysctl, output, args.output_type); + let mut app = App::new(&mut sysctl, output); if args.preload_system_files { app.preload_from_system()?; } else if args.values.is_empty() { - app.display_parameters(args.pattern, args.display_deprecated, args.explain)?; + app.display_parameters(args.pattern, args.explain)?; } else if args.explain { for param in args.values { app.display_documentation(¶m)?; diff --git a/systeroid/src/main.rs b/systeroid/src/main.rs index 49cd4c7..4623c52 100644 --- a/systeroid/src/main.rs +++ b/systeroid/src/main.rs @@ -8,6 +8,9 @@ fn main() { if args.show_tui { let bin = format!("{}-tui", env!("CARGO_PKG_NAME")); let mut command = Command::new(&bin); + if let Some(config) = args.config { + command.arg("--config").arg(config); + } if let Some(kernel_docs) = args.kernel_docs { command.arg("--docs").arg(kernel_docs); } diff --git a/systeroid/src/output.rs b/systeroid/src/output.rs deleted file mode 100644 index 992b0e5..0000000 --- a/systeroid/src/output.rs +++ /dev/null @@ -1,18 +0,0 @@ -/// Possible output types for the [`App`]. -/// -/// [`App`]: crate::app::App -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum OutputType { - /// Print the output as is. - Default, - /// Print the output in a tree-like format. - Tree, - /// Print the output in JSON format. - Json, -} - -impl Default for OutputType { - fn default() -> Self { - Self::Default - } -} diff --git a/systeroid/tests/live_test.rs b/systeroid/tests/live_test.rs index 301bcca..c84d9f7 100644 --- a/systeroid/tests/live_test.rs +++ b/systeroid/tests/live_test.rs @@ -1,7 +1,6 @@ -use { - systeroid::args::Args, systeroid_core::error::Result, - systeroid_core::sysctl::display::DisplayType, -}; +use systeroid::args::Args; +use systeroid_core::error::Result; +use systeroid_core::sysctl::r#type::DisplayType; #[cfg_attr(not(feature = "live-tests"), ignore)] #[test]