chore: merge release-v0.11.0 into main

This commit is contained in:
Jack Wills
2025-08-21 21:48:17 +00:00
34 changed files with 1489 additions and 955 deletions
+13 -3
View File
@@ -1,6 +1,16 @@
### 2025-06-19 ### 2025-08-21
### Reverts ### Chores
+ Bollard update rolled back, closes #66, [aac9c6b598ce6c23b14f5a8b0116e662b18074d2] + Dependencies updated, [ced885e0128b6d5d3a3c7cb97d7e53bc2da64893], [f9b40ea03d0e70e235c28646ff3f9ebb468a904d]
+ Rust 1.89 linting, [79d19ceeb81ae60bc5562683e405d6e74e6f2578]
+ GitHub workflow updated, [08384200558fa1b9d378ea62ea832708caebaa91], [6573af1ed7d382a81c1305397e904066bb8395a8]
### Features
+ Horizontally scroll across logs. By default use `←` & `→` keys to traverse horizontally across the lines when logs panel selected. Updated `config.toml` with `log_scroll_forward` and `log_scroll_back` [c190f0206cc55b8e45b8373f9be954e828c18b3b], [8939ac0345326633e794cc10a981a1f3c5c07549]
+ Force clear screen & redraw of UI. By default uses `f` key, `config.toml` updated with `force_redraw` [50edbc0cc09db864835fe81a03cba8eadafe548b]
+ Increase scroll speed using the `ctrl` key in conjuction with a scroll key, `config.toml` updated with `scroll_modifier`. The next release will remove `scroll_down_many` & `scroll_down_up` keys, [c5bbffdb5f9e800951e4060aa6aee8e00db589aa]
### Refactors
+ remove macos cfg none-const functions, Zigbuild now uses Rust 1.87.0, [eb686e2c952e04da74b3e12c0bfa015ec4615e1d]
see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details
Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 44 KiB

+22 -23
View File
@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v5
# Install stable rust, and associated tools # Install stable rust, and associated tools
- name: install rust - name: install rust
@@ -82,10 +82,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Setup | Artifacts - name: Setup | Artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
- name: Update Release - name: Update Release
uses: ncipollo/release-action@v1 uses: ncipollo/release-action@v1
@@ -107,7 +107,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -122,25 +122,24 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Write release version to env - name: Set up Docker Buildx
run: | uses: docker/setup-buildx-action@v3
CURRENT_SEMVER=${GITHUB_REF_NAME#v}
echo "CURRENT_SEMVER=$CURRENT_SEMVER" >> $GITHUB_ENV
- uses: docker/setup-buildx-action@v3 - name: Build and push Docker image
id: buildx uses: docker/build-push-action@v6
with: with:
install: true context: .
- name: Build for Dockerhub & ghcr.io file: ./containerised/Dockerfile
run: | push: true
docker build --platform linux/arm/v6,linux/arm64,linux/amd64 \ tags: |
-t ${{ secrets.DOCKERHUB_USERNAME }}/oxker:latest \ ${{ secrets.DOCKERHUB_USERNAME }}/{{ github.event.repository.name }}:latest
-t ${{ secrets.DOCKERHUB_USERNAME }}/oxker:${{env.CURRENT_SEMVER}} \ ${{ secrets.DOCKERHUB_USERNAME }}/{{ github.event.repository.name }}:${{env.CURRENT_SEMVER}}
-t ghcr.io/${{ github.repository_owner }}/${{ github.ref_name }}:latest \ ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest
-t ghcr.io/${{ github.repository_owner }}/${{ github.ref_name }}:${{env.CURRENT_SEMVER}} \ ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ env.CURRENT_SEMVER }}
--provenance=false --sbom=false \ platforms: linux/arm/v6,linux/arm64,linux/amd64
--push \ provenance: false
-f containerised/Dockerfile . sbom: false
######################## ########################
# Publish to crates.io # # Publish to crates.io #
######################## ########################
@@ -152,7 +151,7 @@ jobs:
steps: steps:
- name: update rust stable - name: update rust stable
run: rustup update stable run: rustup update stable
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Publish Dry Run - name: Publish Dry Run
run: cargo publish --dry-run run: cargo publish --dry-run
@@ -165,7 +164,7 @@ jobs:
steps: steps:
- name: update rust stable - name: update rust stable
run: rustup update stable run: rustup update stable
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Publish - name: Publish
run: cargo publish run: cargo publish
+1
View File
@@ -1,4 +1,5 @@
/target /target
/releases /releases
/binaries
# Used in the zigbuild for aarch64-apple-darwin # Used in the zigbuild for aarch64-apple-darwin
.intentionally-empty-file.o .intentionally-empty-file.o
+17 -1
View File
@@ -1,3 +1,19 @@
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.11.0'>v0.11.0</a>
### 2025-08-21
### Chores
+ Dependencies updated, [ced885e0](https://github.com/mrjackwills/oxker/commit/ced885e0128b6d5d3a3c7cb97d7e53bc2da64893), [f9b40ea0](https://github.com/mrjackwills/oxker/commit/f9b40ea03d0e70e235c28646ff3f9ebb468a904d)
+ Rust 1.89 linting, [79d19cee](https://github.com/mrjackwills/oxker/commit/79d19ceeb81ae60bc5562683e405d6e74e6f2578)
+ GitHub workflow updated, [08384200](https://github.com/mrjackwills/oxker/commit/08384200558fa1b9d378ea62ea832708caebaa91), [6573af1e](https://github.com/mrjackwills/oxker/commit/6573af1ed7d382a81c1305397e904066bb8395a8)
### Features
+ Horizontally scroll across logs. By default use `←` & `→` keys to traverse horizontally across the lines when logs panel selected. Updated `config.toml` with `log_scroll_forward` and `log_scroll_back` [c190f020](https://github.com/mrjackwills/oxker/commit/c190f0206cc55b8e45b8373f9be954e828c18b3b), [8939ac03](https://github.com/mrjackwills/oxker/commit/8939ac0345326633e794cc10a981a1f3c5c07549)
+ Force clear screen & redraw of UI. By default uses `f` key, `config.toml` updated with `force_redraw` [50edbc0c](https://github.com/mrjackwills/oxker/commit/50edbc0cc09db864835fe81a03cba8eadafe548b)
+ Increase scroll speed using the `ctrl` key in conjuction with a scroll key, `config.toml` updated with `scroll_modifier`. The next release will remove `scroll_down_many` & `scroll_down_up` keys, [c5bbffdb](https://github.com/mrjackwills/oxker/commit/c5bbffdb5f9e800951e4060aa6aee8e00db589aa)
### Refactors
+ remove macos cfg none-const functions, Zigbuild now uses Rust 1.87.0, [eb686e2c](https://github.com/mrjackwills/oxker/commit/eb686e2c952e04da74b3e12c0bfa015ec4615e1d)
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.10.5'>v0.10.5</a> # <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.10.5'>v0.10.5</a>
### 2025-06-19 ### 2025-06-19
@@ -11,7 +27,7 @@
+ .devcontainer updated, [324f8268](https://github.com/mrjackwills/oxker/commit/324f8268278081504d5357f2ed89b78ca2c25d04) + .devcontainer updated, [324f8268](https://github.com/mrjackwills/oxker/commit/324f8268278081504d5357f2ed89b78ca2c25d04)
+ dependencies updated, [0ace9dd6](https://github.com/mrjackwills/oxker/commit/0ace9dd662144a589341779a64d7fcd8de7d9978), [a6360075](https://github.com/mrjackwills/oxker/commit/a636007547280b3b3db69374601dbece4bc21eef) + dependencies updated, [0ace9dd6](https://github.com/mrjackwills/oxker/commit/0ace9dd662144a589341779a64d7fcd8de7d9978), [a6360075](https://github.com/mrjackwills/oxker/commit/a636007547280b3b3db69374601dbece4bc21eef)
+ Rust 1.87.0 linting, [395b1aa7](https://github.com/mrjackwills/oxker/commit/395b1aa7e997a528e4f21e66f5f859001c1c3ec1), [67e5888e](https://github.com/mrjackwills/oxker/commit/67e5888e008cfd504c10e47f678f9351c838be99) + Rust 1.87.0 linting, [395b1aa7](https://github.com/mrjackwills/oxker/commit/395b1aa7e997a528e4f21e66f5f859001c1c3ec1), [67e5888e](https://github.com/mrjackwills/oxker/commit/67e5888e008cfd504c10e47f678f9351c838be99)
back
### Docs ### Docs
+ example config files updated, [63ab7de7](https://github.com/mrjackwills/oxker/commit/63ab7de72897de460f31181c5a42befbee2f91d3), [8fb5ac4a](https://github.com/mrjackwills/oxker/commit/8fb5ac4a945b75f3fcd118c53be1202ccbc43c59) + example config files updated, [63ab7de7](https://github.com/mrjackwills/oxker/commit/63ab7de72897de460f31181c5a42befbee2f91d3), [8fb5ac4a](https://github.com/mrjackwills/oxker/commit/8fb5ac4a945b75f3fcd118c53be1202ccbc43c59)
+ README.md updated, link to directories crate, closes [#65](https://github.com/mrjackwills/oxker/issues/65), [c2bfe329](https://github.com/mrjackwills/oxker/commit/c2bfe3296563daf4b7f077469f3eeff6895720b0) + README.md updated, link to directories crate, closes [#65](https://github.com/mrjackwills/oxker/issues/65), [c2bfe329](https://github.com/mrjackwills/oxker/commit/c2bfe3296563daf4b7f077469f3eeff6895720b0)
Generated
+165 -144
View File
@@ -40,9 +40,9 @@ dependencies = [
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.19" version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
@@ -70,35 +70,41 @@ dependencies = [
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.1.3" version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "3.0.9" version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"once_cell_polyfill", "once_cell_polyfill",
"windows-sys 0.59.0", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.98" version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "backtrace" name = "backtrace"
@@ -123,15 +129,15 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.1" version = "2.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29"
[[package]] [[package]]
name = "bollard" name = "bollard"
version = "0.18.1" version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97ccca1260af6a459d75994ad5acc1651bcabcbdbc41467cc9786519ab854c30" checksum = "8796b390a5b4c86f9f2e8173a68c2791f4fa6b038b84e96dbc01c016d1e6722c"
dependencies = [ dependencies = [
"base64", "base64",
"bollard-stubs", "bollard-stubs",
@@ -162,20 +168,21 @@ dependencies = [
[[package]] [[package]]
name = "bollard-stubs" name = "bollard-stubs"
version = "1.47.1-rc.27.3.1" version = "1.49.0-rc.28.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f179cfbddb6e77a5472703d4b30436bff32929c0aa8a9008ecf23d1d3cdd0da" checksum = "2e7814991259013d5a5bee4ae28657dae0747d843cf06c40f7fc0c2894d6fa38"
dependencies = [ dependencies = [
"serde", "serde",
"serde_json",
"serde_repr", "serde_repr",
"serde_with", "serde_with",
] ]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.18.1" version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]] [[package]]
name = "bytes" name = "bytes"
@@ -197,27 +204,27 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]] [[package]]
name = "castaway" name = "castaway"
version = "0.2.3" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
dependencies = [ dependencies = [
"rustversion", "rustversion",
] ]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.27" version = "1.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.1" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]] [[package]]
name = "chrono" name = "chrono"
@@ -234,9 +241,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.40" version = "4.5.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -244,9 +251,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.40" version = "4.5.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -258,9 +265,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.40" version = "4.5.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@@ -349,7 +356,7 @@ dependencies = [
"document-features", "document-features",
"mio", "mio",
"parking_lot", "parking_lot",
"rustix 1.0.7", "rustix 1.0.8",
"signal-hook", "signal-hook",
"signal-hook-mio", "signal-hook-mio",
"winapi", "winapi",
@@ -473,9 +480,9 @@ dependencies = [
[[package]] [[package]]
name = "dyn-clone" name = "dyn-clone"
version = "1.0.19" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
[[package]] [[package]]
name = "either" name = "either"
@@ -497,12 +504,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.12" version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -519,9 +526,9 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.1" version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [ dependencies = [
"percent-encoding", "percent-encoding",
] ]
@@ -615,9 +622,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.4" version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"equivalent", "equivalent",
@@ -684,19 +691,21 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.6.0" version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
dependencies = [ dependencies = [
"atomic-waker",
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-core",
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
"httpdate", "httpdate",
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
"pin-utils",
"smallvec", "smallvec",
"tokio", "tokio",
"want", "want",
@@ -719,9 +728,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.14" version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@@ -871,9 +880,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "1.0.3" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [ dependencies = [
"idna_adapter", "idna_adapter",
"smallvec", "smallvec",
@@ -903,12 +912,12 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.9.0" version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.4", "hashbrown 0.15.5",
"serde", "serde",
] ]
@@ -931,9 +940,9 @@ dependencies = [
[[package]] [[package]]
name = "instability" name = "instability"
version = "0.3.7" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a"
dependencies = [ dependencies = [
"darling", "darling",
"indoc", "indoc",
@@ -942,6 +951,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "io-uring"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.1" version = "1.70.1"
@@ -1023,15 +1043,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.174" version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.3" version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"libc", "libc",
@@ -1057,9 +1077,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]] [[package]]
name = "litrs" name = "litrs"
version = "0.4.1" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@@ -1083,7 +1103,7 @@ version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
dependencies = [ dependencies = [
"hashbrown 0.15.4", "hashbrown 0.15.5",
] ]
[[package]] [[package]]
@@ -1173,7 +1193,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]] [[package]]
name = "oxker" name = "oxker"
version = "0.10.5" version = "0.11.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bollard", "bollard",
@@ -1228,9 +1248,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
@@ -1285,9 +1305,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.95" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -1309,9 +1329,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.9.1" version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [ dependencies = [
"rand_chacha", "rand_chacha",
"rand_core", "rand_core",
@@ -1359,18 +1379,18 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.13" version = "0.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.5.0" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [ dependencies = [
"getrandom 0.2.16", "getrandom 0.2.16",
"libredox", "libredox",
@@ -1399,9 +1419,9 @@ dependencies = [
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.25" version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]] [[package]]
name = "rustix" name = "rustix"
@@ -1418,22 +1438,22 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.0.7" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.9.4", "linux-raw-sys 0.9.4",
"windows-sys 0.59.0", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.21" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]] [[package]]
name = "ryu" name = "ryu"
@@ -1453,6 +1473,18 @@ dependencies = [
"serde_json", "serde_json",
] ]
[[package]]
name = "schemars"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
dependencies = [
"dyn-clone",
"ref-cast",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
@@ -1481,9 +1513,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.140" version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@@ -1515,9 +1547,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.9" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@@ -1536,16 +1568,17 @@ dependencies = [
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.13.0" version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5"
dependencies = [ dependencies = [
"base64", "base64",
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
"indexmap 2.9.0", "indexmap 2.10.0",
"schemars", "schemars 0.9.0",
"schemars 1.0.4",
"serde", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",
@@ -1590,9 +1623,9 @@ dependencies = [
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.5" version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@@ -1605,9 +1638,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.10" version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
@@ -1617,12 +1650,12 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.10" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -1667,9 +1700,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.103" version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1689,18 +1722,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.12" version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.12" version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1759,20 +1792,22 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.45.1" version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
"io-uring",
"libc", "libc",
"mio", "mio",
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"slab",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -1788,9 +1823,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.15" version = "0.7.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
@@ -1801,35 +1836,32 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.23" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"toml_edit", "toml_parser",
"winnow",
] ]
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.11" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_parser"
version = "0.22.27" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
dependencies = [ dependencies = [
"indexmap 2.9.0",
"serde",
"serde_spanned",
"toml_datetime",
"winnow", "winnow",
] ]
@@ -1945,9 +1977,9 @@ checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.4" version = "2.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" checksum = "137a3c834eaf7139b73688502f3f1141a0337c5d8e4d9b536f9b8c796e26a7c4"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",
@@ -1968,9 +2000,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.17.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be"
dependencies = [ dependencies = [
"getrandom 0.3.3", "getrandom 0.3.3",
"js-sys", "js-sys",
@@ -2147,15 +2179,6 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"
@@ -2171,7 +2194,7 @@ version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [ dependencies = [
"windows-targets 0.53.2", "windows-targets 0.53.3",
] ]
[[package]] [[package]]
@@ -2192,10 +2215,11 @@ dependencies = [
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.53.2" version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [ dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.0", "windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0", "windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0", "windows_i686_gnu 0.53.0",
@@ -2304,12 +2328,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.7.11" version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "wit-bindgen-rt" name = "wit-bindgen-rt"
@@ -2352,18 +2373,18 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.25" version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.25" version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2404,9 +2425,9 @@ dependencies = [
[[package]] [[package]]
name = "zerovec" name = "zerovec"
version = "0.11.2" version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
dependencies = [ dependencies = [
"yoke", "yoke",
"zerofrom", "zerofrom",
+5 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "oxker" name = "oxker"
version = "0.10.5" version = "0.11.0"
edition = "2024" edition = "2024"
authors = ["Jack Wills <email@mrjackwills.com>"] authors = ["Jack Wills <email@mrjackwills.com>"]
description = "A simple tui to view & control docker containers" description = "A simple tui to view & control docker containers"
@@ -27,7 +27,7 @@ similar_names = "allow"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
bollard = "0.18" bollard = "0.19"
cansi = "2.2" cansi = "2.2"
clap = { version = "4.5", features = ["color", "derive", "unicode"] } clap = { version = "4.5", features = ["color", "derive", "unicode"] }
crossterm = "0.29" crossterm = "0.29"
@@ -39,12 +39,12 @@ ratatui = "0.29"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
serde_jsonc = "1.0" serde_jsonc = "1.0"
tokio = { version = "1.45", features = ["full"] } tokio = { version = "1.47", features = ["full"] }
tokio-util = "0.7" tokio-util = "0.7"
toml = { version = "0.8", default-features = false, features = ["parse"] } toml = { version = "0.9", default-features = false, features = ["parse", "serde"] }
tracing = "0.1" tracing = "0.1"
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
uuid = { version = "1.17", features = ["fast-rng", "v4"] } uuid = { version = "1.18", features = ["fast-rng", "v4"] }
[profile.release] [profile.release]
lto = true lto = true
+4 -1
View File
@@ -105,7 +105,9 @@ In application controls, these, amongst many other settings, can be customized w
| button| result| | button| result|
|--|--| |--|--|
| ```( tab )``` or ```( shift+tab )``` | Change panel, clicking on a panel also changes the selected panel.| | ```( tab )``` or ```( shift+tab )``` | Change panel, clicking on a panel also changes the selected panel.|
| ```( ↑ ↓ )``` or ```( j k )``` or ```( PgUp PgDown )``` or ```( Home End )```| Change selected line in selected panel, mouse scroll also changes selected line.| | ```( ↑ ↓ )``` or ```( j k )``` or ```( PgUp PgDown )``` or ```( Home End )```| Scroll line in selected panel - mouse wheel will also scroll.|
| ```( ← → )``` | When logs panel selected, scroll horizontally across the text of the logs.|
| ```( ctrl )``` | Increase scroll speed, used in conjuction scroll keys.|
| ```( enter )```| Run selected docker command.| | ```( enter )```| Run selected docker command.|
| ```( 1-9 )``` | Sort containers by heading, clicking on headings also sorts the selected column. | | ```( 1-9 )``` | Sort containers by heading, clicking on headings also sorts the selected column. |
| ```( 0 )``` | Stop sorting.| | ```( 0 )``` | Stop sorting.|
@@ -113,6 +115,7 @@ In application controls, these, amongst many other settings, can be customized w
| ```( - ) ``` or ```(=)``` | Reduce or increase the height of the logs panel.| | ```( - ) ``` or ```(=)``` | Reduce or increase the height of the logs panel.|
| ```( \ )``` | Toggle the visibility of the logs panel.| | ```( \ )``` | Toggle the visibility of the logs panel.|
| ```( e )``` | Exec into the selected container - not available on Windows.| | ```( e )``` | Exec into the selected container - not available on Windows.|
| ```( f )``` | Force clear the screen & redraw the gui.|
| ```( h )``` | Toggle help menu.| | ```( h )``` | Toggle help menu.|
| ```( m )``` | Toggle mouse capture - if disabled, text on screen can be selected.| | ```( m )``` | Toggle mouse capture - if disabled, text on screen can be selected.|
| ```( q )``` | Quit.| | ```( q )``` | Quit.|
+2 -2
View File
@@ -4,7 +4,7 @@ networks:
name: oxker-examaple-net name: oxker-examaple-net
services: services:
postgres: postgres:
image: postgres:alpine3.21 image: postgres:17-alpine
container_name: postgres container_name: postgres
environment: environment:
- POSTGRES_PASSWORD=never_use_this_password_in_production - POSTGRES_PASSWORD=never_use_this_password_in_production
@@ -18,7 +18,7 @@ services:
limits: limits:
memory: 1024M memory: 1024M
redis: redis:
image: redis:alpine3.21 image: redis:latest
container_name: redis container_name: redis
ipc: private ipc: private
restart: always restart: always
+18
View File
@@ -42,6 +42,7 @@
// 3) F1-F12 // 3) F1-F12
// 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down // 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down
// Each definition can have two keys associated with it // Each definition can have two keys associated with it
// WARNING "scroll_many" only accepts control, alt, shift, with no secondary option
// If any key clashes are found, oxker will revert to it's default keymap // If any key clashes are found, oxker will revert to it's default keymap
"keymap": { "keymap": {
// Clear any popup boxes, filter panel, or help panel // Clear any popup boxes, filter panel, or help panel
@@ -74,10 +75,12 @@
"save_logs": [ "save_logs": [
"s" "s"
], ],
// TODO "scroll_down_many" will be removed in the next release
// Scroll down a list by many // Scroll down a list by many
"scroll_down_many": [ "scroll_down_many": [
"pagedown" "pagedown"
], ],
// TODO rename in next release
// Scroll down a list by one item // Scroll down a list by one item
"scroll_down_one": [ "scroll_down_one": [
"down", "down",
@@ -87,19 +90,30 @@
"scroll_end": [ "scroll_end": [
"end" "end"
], ],
// Modifier to scroll by 10 lines isntead of one, used in conjunction with scroll_up_x/scroll_down_x
"scroll_many": ["control"],
// Scroll up to the start of a list // Scroll up to the start of a list
"scroll_start": [ "scroll_start": [
"home" "home"
], ],
// TODO "scroll_up_many" will be removed in the next release
// Scroll up a list by many // Scroll up a list by many
"scroll_up_many": [ "scroll_up_many": [
"pageup" "pageup"
], ],
// TODO rename in next release
// Scroll up a list by one item // Scroll up a list by one item
"scroll_up_one": [ "scroll_up_one": [
"up", "up",
"k" "k"
], ],
// Horizontal scroll of the logs
"log_scroll_forward": [
"right"
],
"log_scroll_back": [
"left"
],
// Select next panel // Select next panel
"select_next_panel": [ "select_next_panel": [
"tab" "tab"
@@ -159,6 +173,10 @@
// Toggle visibility of the log section // Toggle visibility of the log section
"log_section_toggle": [ "log_section_toggle": [
"\\" "\\"
],
// Force a complete clear & redraw of the screen
"force_redraw": [
"f"
] ]
}, },
//////////////////// ////////////////////
+16 -2
View File
@@ -55,8 +55,10 @@ show_logs = true
# 3) F1-F12 # 3) F1-F12
# 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down # 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down
# Each definition can have two keys associated with it # Each definition can have two keys associated with it
# WARNING "scroll_many" only accepts control, alt, shift, with no secondary option
# If any key clashes are found, oxker will revert to it's default keymap # If any key clashes are found, oxker will revert to it's default keymap
[keymap] [keymap]
@@ -74,18 +76,28 @@ filter_mode = ["/", "F1"]
quit = ["q"] quit = ["q"]
# Save logs of selected container to file on disk # Save logs of selected container to file on disk
save_logs = ["s"] save_logs = ["s"]
# TODO "scroll_down_many" will be removed in the next release
# scroll down a list by many # scroll down a list by many
scroll_down_many = ["pagedown"] scroll_down_many = ["pagedown"]
# TODO rename in next release
# scroll down a list by one item # scroll down a list by one item
scroll_down_one = ["down", "j"] scroll_down_one = ["down", "j"]
# scroll down to the end of a list # scroll down to the end of a list
scroll_end = ["end"] scroll_end = ["end"]
# Modifier to scroll by 10 lines isntead of one, used in conjunction with scroll_up_x/scroll_down_x
scroll_many = ["control"]
# scroll up to the start of a list # scroll up to the start of a list
scroll_start = ["home"] scroll_start = ["home"]
# TODO "scroll_up_many" will be removed in the next release
# scroll up a list by many # scroll up a list by many
scroll_up_many = ["pageup"] scroll_up_many = ["pageup"]
# TODO rename in next release
# scroll up a list by one item # scroll up a list by one item
scroll_up_one = ["up", "k"] scroll_up_one = ["up", "k"]
# Horizontal scroll of the logs
log_scroll_forward = ["right"]
log_scroll_back = ["left"]
# Select next panel # Select next panel
select_next_panel = ["tab"] select_next_panel = ["tab"]
# Select previous panel # Select previous panel
@@ -108,10 +120,11 @@ toggle_help = ["h"]
toggle_mouse_capture = ["m"] toggle_mouse_capture = ["m"]
# Reduce the height of the logs list section # Reduce the height of the logs list section
log_section_height_decrease = ["-"] log_section_height_decrease = ["-"]
# Increase the height of the logs list section
log_section_height_increase = ["+"] log_section_height_increase = ["+"]
# Toggle visibility of the log section # Toggle visibility of the log section
log_section_toggle = ["\\"] log_section_toggle = ["\\"]
# Force a complete clear & redraw of the screen
force_redraw = ["f"]
################# #################
# Custom Colors # # Custom Colors #
@@ -186,6 +199,7 @@ selected_filter_text = "black"
# Highlighted text color # Highlighted text color
highlight = "magenta" highlight = "magenta"
# The color the of Docker commands available for each container # The color the of Docker commands available for each container
[colors.commands] [colors.commands]
# Background color of panel # Background color of panel
+257 -54
View File
@@ -8,8 +8,10 @@ use std::{
use bollard::service::Port; use bollard::service::Port;
use jiff::{Timestamp, tz::TimeZone}; use jiff::{Timestamp, tz::TimeZone};
use ratatui::{ use ratatui::{
layout::Size,
style::Color, style::Color,
widgets::{ListItem, ListState}, text::{Line, Text},
widgets::ListState,
}; };
use crate::config::AppColors; use crate::config::AppColors;
@@ -30,14 +32,6 @@ impl From<&str> for ContainerId {
} }
impl ContainerId { impl ContainerId {
// TODO remove this once zigbuild uses Rust v1.87.0
#[cfg(target_os = "macos")]
#[allow(clippy::missing_const_for_fn)]
pub fn get(&self) -> &str {
self.0.as_str()
}
#[cfg(not(target_os = "macos"))]
pub const fn get(&self) -> &str { pub const fn get(&self) -> &str {
self.0.as_str() self.0.as_str()
} }
@@ -84,14 +78,6 @@ macro_rules! unit_struct {
} }
impl $name { impl $name {
#[cfg(target_os = "macos")]
#[allow(clippy::missing_const_for_fn)]
// TODO remove this once zigbuild uses Rust v1.87.0
pub fn get(&self) -> &str {
self.0.as_str()
}
#[cfg(not(target_os = "macos"))]
pub const fn get(&self) -> &str { pub const fn get(&self) -> &str {
self.0.as_str() self.0.as_str()
} }
@@ -215,6 +201,7 @@ impl<T> StatefulList<T> {
} }
/// Return the current status of the select list, e.g. 2/5, /// Return the current status of the select list, e.g. 2/5,
/// MAYBE add up down arrows, check if at start or end etc
pub fn get_state_title(&self) -> String { pub fn get_state_title(&self) -> String {
if self.items.is_empty() { if self.items.is_empty() {
String::new() String::new()
@@ -342,6 +329,54 @@ impl From<(&str, &ContainerStatus)> for State {
} }
} }
/// Need status, to check if container is unhealthy or not
impl
From<(
&bollard::secret::ContainerSummaryStateEnum,
&ContainerStatus,
)> for State
{
fn from(
(input, status): (
&bollard::secret::ContainerSummaryStateEnum,
&ContainerStatus,
),
) -> Self {
match input {
bollard::secret::ContainerSummaryStateEnum::DEAD => Self::Dead,
bollard::secret::ContainerSummaryStateEnum::EXITED => Self::Exited,
bollard::secret::ContainerSummaryStateEnum::PAUSED => Self::Paused,
bollard::secret::ContainerSummaryStateEnum::REMOVING => Self::Removing,
bollard::secret::ContainerSummaryStateEnum::RESTARTING => Self::Restarting,
bollard::secret::ContainerSummaryStateEnum::RUNNING => {
if status.unhealthy() {
Self::Running(RunningState::Unhealthy)
} else {
Self::Running(RunningState::Healthy)
}
}
_ => Self::Unknown,
}
}
}
/// Again, need status, to check if container is unhealthy or not
impl
From<(
Option<&bollard::secret::ContainerSummaryStateEnum>,
&ContainerStatus,
)> for State
{
fn from(
(input, status): (
Option<&bollard::secret::ContainerSummaryStateEnum>,
&ContainerStatus,
),
) -> Self {
input.map_or(Self::Unknown, |input| Self::from((input, status)))
}
}
/// Again, need status, to check if container is unhealthy or not /// Again, need status, to check if container is unhealthy or not
impl From<(Option<String>, &ContainerStatus)> for State { impl From<(Option<String>, &ContainerStatus)> for State {
fn from((input, status): (Option<String>, &ContainerStatus)) -> Self { fn from((input, status): (Option<String>, &ContainerStatus)) -> Self {
@@ -563,81 +598,177 @@ impl LogsTz {
/// stateful list dependent on whether the timestamp is in the HashSet or not /// stateful list dependent on whether the timestamp is in the HashSet or not
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Logs { pub struct Logs {
logs: StatefulList<ListItem<'static>>, lines: StatefulList<Text<'static>>,
tz: HashSet<LogsTz>, tz: HashSet<LogsTz>,
offset: u16,
max_log_len: usize,
adjusted_max_width: usize,
adjust_max_width_text_len: usize,
} }
impl Default for Logs { impl Default for Logs {
fn default() -> Self { fn default() -> Self {
let mut logs = StatefulList::new(vec![]); let mut lines = StatefulList::new(vec![]);
logs.end(); lines.end();
Self { Self {
logs, lines,
tz: HashSet::new(), tz: HashSet::new(),
offset: 0,
adjusted_max_width: 0,
adjust_max_width_text_len: 0,
max_log_len: 0,
} }
} }
} }
impl Logs { impl Logs {
/// Only allow a new log line to be inserted if the log timestamp isn't in the tz HashSet /// Only allow a new log line to be inserted if the log timestamp isn't in the tz HashSet
pub fn insert(&mut self, line: ListItem<'static>, tz: LogsTz) { pub fn insert(&mut self, line: Text<'static>, tz: LogsTz) {
if self.tz.insert(tz) { if self.tz.insert(tz) {
self.logs.items.push(line); self.max_log_len = self.max_log_len.max(line.width());
self.lines.items.push(line);
} }
} }
/// Get the logs vec, but instead of cloning to whole vec, only clone items with x of the currently selected index /// If scrolling horiztonally along the logs, display a counter of the position in the in the scroll, `x/y`
pub fn get_scroll_title(&mut self, width: u16) -> Option<String> {
if self.horizontal_scroll_able(width) {
let text_width = self.adjust_max_width_text_len;
let arrow_left = if self.offset > 0 { "" } else { " " };
let arrow_right = if usize::from(self.offset) < self.adjusted_max_width {
""
} else {
" "
};
Some(format!(
"{left} {offset:>text_width$}/{adjusted_max_width} {right}",
offset = self.offset,
adjusted_max_width = self.adjusted_max_width,
left = arrow_left,
right = arrow_right,
))
} else {
None
}
}
/// Format a log lone. Only return screen width amount of chars
/// If offset set, remove `char_offset` number of chars from a Text
/// `text` *should* only be a single line, so just use the .first() method rather than trying to iterate
fn format_log_line(text: &Text<'static>, char_offset: usize, width: u16) -> Text<'static> {
let mut skipped = 0;
text.lines.first().map_or_else(Text::default, |line| {
Text::from(Line::from(
line.spans
.iter()
.filter_map(|span| {
if skipped >= char_offset {
Some(ratatui::text::Span::styled(
span.content.chars().take(width.into()).collect::<String>(),
span.style,
))
} else {
let span_len = span.content.chars().count();
if skipped + span_len <= char_offset {
skipped += span_len;
None
} else {
let start_index = char_offset - skipped;
skipped = char_offset;
Some(ratatui::text::Span::styled(
span.content
.chars()
.skip(start_index)
.take(width.into())
.collect::<String>(),
span.style,
))
}
}
})
.collect::<Vec<_>>(),
))
})
}
/// Get the logs vec, but instead of cloning to whole vec, only clone items within x of the currently selected index, as well as only the current screen widths number of chars
/// Where x is the abs different of the index plus the panel height & a padding /// Where x is the abs different of the index plus the panel height & a padding
/// Take into account the char offset, so that can scroll a line
/// The rest can be just empty list items /// The rest can be just empty list items
pub fn to_vec(&self, height: usize, padding: usize) -> Vec<ListItem<'static>> { pub fn get_visible_logs(&self, size: Size, padding: usize) -> Vec<Text<'static>> {
let current_index = self.logs.state.selected().unwrap_or_default(); let current_index = self.lines.state.selected().unwrap_or_default();
self.logs let height_padding = usize::from(size.height) + padding;
let char_offset = if usize::from(self.offset) > self.max_log_len {
self.max_log_len
} else {
self.offset.into()
};
self.lines
.items .items
.iter() .iter()
.enumerate() .enumerate()
.map(|(index, item)| { .map(|(index, item)| {
if current_index.abs_diff(index) <= height + padding { if current_index.abs_diff(index) <= height_padding {
item.clone() Self::format_log_line(item, char_offset, size.width)
} else { } else {
ListItem::from("") Text::from("")
} }
}) })
.collect() .collect()
} }
/// The rest of the methods are basically forwarding from the underlying StatefulList /// The rest of the methods are basically forwarding from the underlying StatefulList
pub fn get_state_title(&self) -> String { pub fn get_state_title(&self) -> String {
self.logs.get_state_title() self.lines.get_state_title()
}
/// Return true it currently selected cotnainer logs are wide enough to horizontally scroll
pub fn horizontal_scroll_able(&mut self, width: u16) -> bool {
if self.lines.items.is_empty() {
return false;
}
self.adjusted_max_width = self.max_log_len.saturating_sub(width.into()) + 4;
self.adjust_max_width_text_len = self.adjusted_max_width.to_string().chars().count();
self.max_log_len + 4 > usize::from(width)
}
/// Add a padding so one char will always be visilbe?
pub fn forward(&mut self, width: u16) {
let offset = usize::from(self.offset);
if self.horizontal_scroll_able(width) {
if self.adjusted_max_width > 0 && offset < self.adjusted_max_width {
self.offset = self.offset.saturating_add(1);
}
}
}
/// Reduce the char offset
pub const fn back(&mut self) {
self.offset = self.offset.saturating_sub(1);
} }
pub fn next(&mut self) { pub fn next(&mut self) {
self.logs.next(); self.lines.next();
} }
pub fn previous(&mut self) { pub fn previous(&mut self) {
self.logs.previous(); self.lines.previous();
} }
pub fn end(&mut self) { pub fn end(&mut self) {
self.logs.end(); self.lines.end();
} }
pub fn start(&mut self) { pub fn start(&mut self) {
self.logs.start(); self.lines.start();
} }
// TODO remove this once zigbuild uses Rust v1.87.0
#[cfg(target_os = "macos")]
#[allow(clippy::missing_const_for_fn)]
pub fn len(&self) -> usize {
self.logs.items.len()
}
#[cfg(not(target_os = "macos"))]
pub const fn len(&self) -> usize { pub const fn len(&self) -> usize {
self.logs.items.len() self.lines.items.len()
} }
pub const fn state(&mut self) -> &mut ListState { pub const fn state(&mut self) -> &mut ListState {
&mut self.logs.state &mut self.lines.state
} }
} }
@@ -801,7 +932,10 @@ impl Columns {
mod tests { mod tests {
use jiff::tz::TimeZone; use jiff::tz::TimeZone;
use ratatui::widgets::ListItem; use ratatui::{
layout::Size,
text::{Line, Text},
};
use crate::{ use crate::{
app_data::{ContainerImage, Logs, LogsTz, RunningState}, app_data::{ContainerImage, Logs, LogsTz, RunningState},
@@ -941,21 +1075,21 @@ mod tests {
let mut logs = Logs::default(); let mut logs = Logs::default();
let line = log_sanitizer::remove_ansi(input); let line = log_sanitizer::remove_ansi(input);
logs.insert(ListItem::new(line.clone()), tz.clone()); logs.insert(Text::from(line.clone()), tz.clone());
logs.insert(ListItem::new(line.clone()), tz.clone()); logs.insert(Text::from(line.clone()), tz.clone());
logs.insert(ListItem::new(line), tz); logs.insert(Text::from(line), tz);
assert_eq!(logs.logs.items.len(), 1); assert_eq!(logs.lines.items.len(), 1);
let input = "2023-01-15T19:13:30.783138328Z Lorem ipsum dolor sit amet"; let input = "2023-01-15T19:13:30.783138328Z Lorem ipsum dolor sit amet";
let (tz, _) = LogsTz::splitter(input); let (tz, _) = LogsTz::splitter(input);
let line = log_sanitizer::remove_ansi(input); let line = log_sanitizer::remove_ansi(input);
logs.insert(ListItem::new(line.clone()), tz.clone()); logs.insert(Text::from(line.clone()), tz.clone());
logs.insert(ListItem::new(line.clone()), tz.clone()); logs.insert(Text::from(line.clone()), tz.clone());
logs.insert(ListItem::new(line), tz); logs.insert(Text::from(line), tz);
assert_eq!(logs.logs.items.len(), 2); assert_eq!(logs.lines.items.len(), 2);
} }
#[test] #[test]
@@ -1008,4 +1142,73 @@ mod tests {
let input = State::from(("oxker", &healthy)); let input = State::from(("oxker", &healthy));
assert_eq!(input, State::Unknown); assert_eq!(input, State::Unknown);
} }
#[test]
/// Test the format_log_line methods, should ideally check colours are being correct kept as well
fn test_to_vec() {
let mut logs = Logs::default();
let input = "2023-01-14T19:13:30.783138328Z Hello world some long line".to_owned();
let (tz, _) = LogsTz::splitter(&input);
logs.insert(Text::from(input), tz);
let input = "2023-01-14T19:13:31.783138328Z Hello world some line".to_owned();
let (tz, _) = LogsTz::splitter(&input);
logs.insert(Text::from(input), tz);
let input = "2023-01-14T19:13:32.783138328Z Hello world".to_owned();
let (tz, _) = LogsTz::splitter(&input);
logs.insert(Text::from(input), tz);
logs.offset = 43;
let result = logs.get_visible_logs(
Size {
width: 14,
height: 10,
},
10,
);
assert_eq!(
vec![
Text::from(Line::from("some long line")),
Text::from(Line::from("some line")),
Text::from(Line::default())
],
result
);
}
#[test]
/// Test the get_scroll_title methods
fn test_scroll_title() {
let mut logs = Logs::default();
let result = logs.get_scroll_title(10);
assert!(result.is_none());
let input = "short".to_owned();
let (tz, _) = LogsTz::splitter(&input);
logs.insert(Text::from(input), tz);
let result = logs.get_scroll_title(10);
assert!(result.is_none());
let input = "2023-01-14T19:13:30.783138328Z Hello world some long line".to_owned();
let (tz, _) = LogsTz::splitter(&input);
logs.insert(Text::from(input), tz);
let result = logs.get_scroll_title(10);
assert_eq!(result, Some(" 0/51 → ".to_owned()));
logs.forward(10);
let result = logs.get_scroll_title(10);
assert_eq!(result, Some(" ← 1/51 → ".to_owned()));
for _ in 0..=49 {
logs.forward(10);
}
let result = logs.get_scroll_title(10);
assert_eq!(result, Some(" ← 51/51 ".to_owned()));
}
} }
+100 -51
View File
@@ -1,7 +1,7 @@
use bollard::models::ContainerSummary; use bollard::models::ContainerSummary;
use core::fmt; use core::fmt;
use parking_lot::Mutex; use parking_lot::Mutex;
use ratatui::widgets::{ListItem, ListState}; use ratatui::{layout::Size, text::Text, widgets::ListState};
use std::{ use std::{
hash::Hash, hash::Hash,
sync::Arc, sync::Arc,
@@ -122,7 +122,7 @@ pub struct AppData {
error: Option<AppError>, error: Option<AppError>,
filter: Filter, filter: Filter,
hidden_containers: Vec<ContainerItem>, hidden_containers: Vec<ContainerItem>,
redraw: Arc<Rerender>, rerender: Arc<Rerender>,
sorted_by: Option<(Header, SortedOrder)>, sorted_by: Option<(Header, SortedOrder)>,
current_sorted_id: Vec<ContainerId>, current_sorted_id: Vec<ContainerId>,
pub config: Config, pub config: Config,
@@ -137,7 +137,7 @@ pub struct AppData {
pub filter: Filter, pub filter: Filter,
pub hidden_containers: Vec<ContainerItem>, pub hidden_containers: Vec<ContainerItem>,
pub current_sorted_id: Vec<ContainerId>, pub current_sorted_id: Vec<ContainerId>,
pub redraw: Arc<Rerender>, pub rerender: Arc<Rerender>,
pub sorted_by: Option<(Header, SortedOrder)>, pub sorted_by: Option<(Header, SortedOrder)>,
} }
@@ -151,7 +151,7 @@ impl AppData {
error: None, error: None,
filter: Filter::new(), filter: Filter::new(),
hidden_containers: vec![], hidden_containers: vec![],
redraw: Arc::clone(redraw), rerender: Arc::clone(redraw),
sorted_by: None, sorted_by: None,
} }
} }
@@ -192,7 +192,7 @@ impl AppData {
/// sets the state to start if any filtering has occurred /// sets the state to start if any filtering has occurred
/// Also search in the "hidden" vec for items and insert back into the main containers vec /// Also search in the "hidden" vec for items and insert back into the main containers vec
fn filter_containers(&mut self) { fn filter_containers(&mut self) {
self.redraw.update(); self.rerender.update_draw();
let pre_len = self.get_container_len(); let pre_len = self.get_container_len();
if !self.hidden_containers.is_empty() { if !self.hidden_containers.is_empty() {
@@ -296,7 +296,7 @@ impl AppData {
/// Remove the sorted header & order, and sort by default - created datetime /// Remove the sorted header & order, and sort by default - created datetime
pub fn reset_sorted(&mut self) { pub fn reset_sorted(&mut self) {
self.set_sorted(None); self.set_sorted(None);
self.redraw.update(); self.rerender.update_draw();
} }
/// Sort containers based on a given header, if headings match, and already ascending, remove sorting /// Sort containers based on a given header, if headings match, and already ascending, remove sorting
@@ -392,7 +392,7 @@ impl AppData {
self.containers.items.sort_by(sort_closure); self.containers.items.sort_by(sort_closure);
if pre_order != self.get_current_ids() { if pre_order != self.get_current_ids() {
self.redraw.update(); self.rerender.update_draw();
} }
} else if self.current_sorted_id != self.get_current_ids() { } else if self.current_sorted_id != self.get_current_ids() {
self.containers.items.sort_by(|a, b| { self.containers.items.sort_by(|a, b| {
@@ -400,20 +400,13 @@ impl AppData {
.cmp(&b.created) .cmp(&b.created)
.then_with(|| a.name.get().cmp(b.name.get())) .then_with(|| a.name.get().cmp(b.name.get()))
}); });
self.redraw.update(); self.rerender.update_draw();
self.current_sorted_id = self.get_current_ids(); self.current_sorted_id = self.get_current_ids();
} }
} }
/// Container state methods /// Container state methods
/// Get the total number of none "hidden" containers /// Get the total number of none "hidden" containers
// TODO remove this once zigbuild uses Rust v1.87.0
#[cfg(target_os = "macos")]
pub fn get_container_len(&self) -> usize {
self.containers.items.len()
}
#[cfg(not(target_os = "macos"))]
pub const fn get_container_len(&self) -> usize { pub const fn get_container_len(&self) -> usize {
self.containers.items.len() self.containers.items.len()
} }
@@ -446,25 +439,25 @@ impl AppData {
/// Select the first container /// Select the first container
pub fn containers_start(&mut self) { pub fn containers_start(&mut self) {
self.containers.start(); self.containers.start();
self.redraw.update(); self.rerender.update_draw();
} }
/// select the last container /// select the last container
pub fn containers_end(&mut self) { pub fn containers_end(&mut self) {
self.containers.end(); self.containers.end();
self.redraw.update(); self.rerender.update_draw();
} }
/// Select the next container /// Select the next container
pub fn containers_next(&mut self) { pub fn containers_next(&mut self) {
self.containers.next(); self.containers.next();
self.redraw.update(); self.rerender.update_draw();
} }
/// select the previous container /// select the previous container
pub fn containers_previous(&mut self) { pub fn containers_previous(&mut self) {
self.containers.previous(); self.containers.previous();
self.redraw.update(); self.rerender.update_draw();
} }
/// Get ListState of containers /// Get ListState of containers
@@ -586,7 +579,7 @@ impl AppData {
pub fn docker_controls_next(&mut self) { pub fn docker_controls_next(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.next(); i.docker_controls.next();
self.redraw.update(); self.rerender.update_draw();
} }
} }
@@ -594,7 +587,7 @@ impl AppData {
pub fn docker_controls_previous(&mut self) { pub fn docker_controls_previous(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.previous(); i.docker_controls.previous();
self.redraw.update(); self.rerender.update_draw();
} }
} }
@@ -602,7 +595,7 @@ impl AppData {
pub fn docker_controls_start(&mut self) { pub fn docker_controls_start(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.start(); i.docker_controls.start();
self.redraw.update(); self.rerender.update_draw();
} }
} }
@@ -610,7 +603,7 @@ impl AppData {
pub fn docker_controls_end(&mut self) { pub fn docker_controls_end(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.end(); i.docker_controls.end();
self.redraw.update(); self.rerender.update_draw();
} }
} }
@@ -644,11 +637,33 @@ impl AppData {
}) })
} }
/// If scrolling horiztonally along the logs, display a counter of the position in the in the scroll, `x/y`
pub fn get_scroll_title(&mut self, width: u16) -> Option<String> {
self.get_mut_selected_container()
.and_then(|i| i.logs.get_scroll_title(width))
}
/// Increase the logs offset, basically moving an invisible cursor back
pub fn log_back(&mut self) {
if let Some(i) = self.get_mut_selected_container() {
i.logs.back();
self.rerender.update_draw();
}
}
/// Increase the logs offset, basically moving an invisible cursor forward
pub fn log_forward(&mut self, width: u16) {
if let Some(i) = self.get_mut_selected_container() {
i.logs.forward(width);
self.rerender.update_draw();
}
}
/// select next selected log line /// select next selected log line
pub fn log_next(&mut self) { pub fn log_next(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.logs.next(); i.logs.next();
self.redraw.update(); self.rerender.update_draw();
} }
} }
@@ -656,7 +671,7 @@ impl AppData {
pub fn log_previous(&mut self) { pub fn log_previous(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.logs.previous(); i.logs.previous();
self.redraw.update(); self.rerender.update_draw();
} }
} }
@@ -664,7 +679,7 @@ impl AppData {
pub fn log_end(&mut self) { pub fn log_end(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.logs.end(); i.logs.end();
self.redraw.update(); self.rerender.update_draw();
} }
} }
@@ -672,17 +687,17 @@ impl AppData {
pub fn log_start(&mut self) { pub fn log_start(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.logs.start(); i.logs.start();
self.redraw.update(); self.rerender.update_draw();
} }
} }
/// Get mutable Vec of current containers logs /// Get mutable Vec of current containers logs
pub fn get_logs(&self, height: u16, padding: usize) -> Vec<ListItem<'static>> { pub fn get_logs(&self, size: Size, padding: usize) -> Vec<Text<'static>> {
self.containers self.containers
.state .state
.selected() .selected()
.and_then(|i| self.containers.items.get(i)) .and_then(|i| self.containers.items.get(i))
.map_or(vec![], |i| i.logs.to_vec(height.into(), padding)) .map_or(vec![], |i| i.logs.get_visible_logs(size, padding))
} }
/// Get mutable Option of the currently selected container Logs state /// Get mutable Option of the currently selected container Logs state
@@ -713,14 +728,14 @@ impl AppData {
/// Remove single app_state error /// Remove single app_state error
pub fn remove_error(&mut self) { pub fn remove_error(&mut self) {
self.error = None; self.error = None;
self.redraw.update(); self.rerender.update_draw();
} }
/// Insert single app_state error /// Insert single app_state error
pub fn set_error(&mut self, error: AppError, gui_state: &Arc<Mutex<GuiState>>, status: Status) { pub fn set_error(&mut self, error: AppError, gui_state: &Arc<Mutex<GuiState>>, status: Status) {
gui_state.lock().status_push(status); gui_state.lock().status_push(status);
self.error = Some(error); self.error = Some(error);
self.redraw.update(); self.rerender.update_draw();
} }
/// Check if the selected container is a dockerised version of oxker /// Check if the selected container is a dockerised version of oxker
@@ -810,7 +825,7 @@ impl AppData {
container.mem_limit.update(mem_limit); container.mem_limit.update(mem_limit);
} }
if self.is_selected_container(id) { if self.is_selected_container(id) {
self.redraw.update(); self.rerender.update_draw();
} }
self.sort_containers(); self.sort_containers();
} }
@@ -848,7 +863,7 @@ impl AppData {
if self.containers.items.get(index).is_some() { if self.containers.items.get(index).is_some() {
self.containers.items.remove(index); self.containers.items.remove(index);
if self.is_selected_container(id) { if self.is_selected_container(id) {
self.redraw.update(); self.rerender.update_draw();
} }
} }
} }
@@ -881,7 +896,12 @@ impl AppData {
.as_ref() .as_ref()
.map_or(String::new(), std::clone::Clone::clone), .map_or(String::new(), std::clone::Clone::clone),
); );
let state = State::from((i.state.as_ref().map_or("dead", |z| z), &status)); let state = State::from((
i.state
.as_ref()
.map_or(&bollard::secret::ContainerSummaryStateEnum::DEAD, |z| z),
&status,
));
let image = i let image = i
.image .image
.as_ref() .as_ref()
@@ -965,7 +985,7 @@ impl AppData {
} else { } else {
log_sanitizer::remove_ansi(&i) log_sanitizer::remove_ansi(&i)
}; };
container.logs.insert(ListItem::new(lines), log_tz); container.logs.insert(Text::from(lines), log_tz);
} }
// Set the logs selected row for each container // Set the logs selected row for each container
@@ -977,7 +997,7 @@ impl AppData {
} }
} }
if self.is_selected_container(id) { if self.is_selected_container(id) {
self.redraw.update(); self.rerender.update_draw();
} }
} }
} }
@@ -1945,14 +1965,19 @@ mod tests {
let logs = (1..=3).map(|i| format!("{i} {i}")).collect::<Vec<_>>(); let logs = (1..=3).map(|i| format!("{i} {i}")).collect::<Vec<_>>();
app_data.update_log_by_id(logs, &ids[0]); app_data.update_log_by_id(logs, &ids[0]);
// app_data.log_start();
let result = app_data.get_log_state(); let result = app_data.get_log_state();
assert!(result.is_some()); assert!(result.is_some());
assert_eq!(result.as_ref().unwrap().selected(), Some(2)); assert_eq!(result.as_ref().unwrap().selected(), Some(2));
assert_eq!(result.unwrap().offset(), 0); assert_eq!(result.unwrap().offset(), 0);
let result = app_data.get_logs(4, 1); let result = app_data.get_logs(
Size {
width: 20,
height: 4,
},
1,
);
assert_eq!(result.len(), 3); assert_eq!(result.len(), 3);
let result = app_data.get_log_title(); let result = app_data.get_log_title();
@@ -2340,44 +2365,68 @@ mod tests {
app_data.update_log_by_id(logs, &ids[0]); app_data.update_log_by_id(logs, &ids[0]);
let result = app_data.get_logs(10, 10); let result = app_data.get_logs(
Size {
width: 20,
height: 10,
},
10,
);
for (index, item) in result.iter().enumerate() { for (index, item) in result.iter().enumerate() {
if index < 979 { if index < 979 {
assert_eq!(item, &ListItem::new("")); assert_eq!(item, &Text::from(""));
} else { } else {
assert_eq!(item, &ListItem::new(format!("{index}"))); assert_eq!(item, &Text::from(format!("{index}")));
} }
} }
let result = app_data.get_logs(100, 20); let result = app_data.get_logs(
Size {
width: 20,
height: 100,
},
20,
);
for (index, item) in result.iter().enumerate() { for (index, item) in result.iter().enumerate() {
if index < 879 { if index < 879 {
assert_eq!(item, &ListItem::new("")); assert_eq!(item, &Text::from(""));
} else { } else {
assert_eq!(item, &ListItem::new(format!("{index}"))); assert_eq!(item, &Text::from(format!("{index}")));
} }
} }
app_data.log_start(); app_data.log_start();
let result = app_data.get_logs(10, 10);
let result = app_data.get_logs(
Size {
width: 20,
height: 10,
},
10,
);
for (index, item) in result.iter().enumerate() { for (index, item) in result.iter().enumerate() {
if index > 20 { if index > 20 {
assert_eq!(item, &ListItem::new("")); assert_eq!(item, &Text::from(""));
} else { } else {
assert_eq!(item, &ListItem::new(format!("{index}"))); assert_eq!(item, &Text::from(format!("{index}")));
} }
} }
for _ in 0..=500 { for _ in 0..=500 {
app_data.log_next(); app_data.log_next();
} }
let result = app_data.get_logs(
let result = app_data.get_logs(10, 10); Size {
width: 20,
height: 10,
},
10,
);
for (index, item) in result.iter().enumerate() { for (index, item) in result.iter().enumerate() {
if (481..=521).contains(&index) { if (481..=521).contains(&index) {
assert_eq!(item, &ListItem::new(format!("{index}"))); assert_eq!(item, &Text::from(format!("{index}")));
} else { } else {
assert_eq!(item, &ListItem::new("")); assert_eq!(item, &Text::from(""));
} }
} }
} }
+15
View File
@@ -56,6 +56,9 @@ show_logs = true
# 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down # 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down
# Each definition can have two keys associated with it # Each definition can have two keys associated with it
# WARNING "scroll_many" only accepts control, alt, shift, with no secondary option
# If any key clashes are found, oxker will revert to it's default keymap # If any key clashes are found, oxker will revert to it's default keymap
[keymap] [keymap]
@@ -73,18 +76,28 @@ filter_mode = ["/", "F1"]
quit = ["q"] quit = ["q"]
# Save logs of selected container to file on disk # Save logs of selected container to file on disk
save_logs = ["s"] save_logs = ["s"]
# TODO "scroll_down_many" will be removed in the next release
# scroll down a list by many # scroll down a list by many
scroll_down_many = ["pagedown"] scroll_down_many = ["pagedown"]
# TODO rename in next release
# scroll down a list by one item # scroll down a list by one item
scroll_down_one = ["down", "j"] scroll_down_one = ["down", "j"]
# scroll down to the end of a list # scroll down to the end of a list
scroll_end = ["end"] scroll_end = ["end"]
# Modifier to scroll by 10 lines isntead of one, used in conjunction with scroll_up_x/scroll_down_x
scroll_many = ["control"]
# scroll up to the start of a list # scroll up to the start of a list
scroll_start = ["home"] scroll_start = ["home"]
# TODO "scroll_up_many" will be removed in the next release
# scroll up a list by many # scroll up a list by many
scroll_up_many = ["pageup"] scroll_up_many = ["pageup"]
# TODO rename in next release
# scroll up a list by one item # scroll up a list by one item
scroll_up_one = ["up", "k"] scroll_up_one = ["up", "k"]
# Horizontal scroll of the logs
log_scroll_forward = ["right"]
log_scroll_back = ["left"]
# Select next panel # Select next panel
select_next_panel = ["tab"] select_next_panel = ["tab"]
# Select previous panel # Select previous panel
@@ -110,6 +123,8 @@ log_section_height_decrease = ["-"]
log_section_height_increase = ["+"] log_section_height_increase = ["+"]
# Toggle visibility of the log section # Toggle visibility of the log section
log_section_toggle = ["\\"] log_section_toggle = ["\\"]
# Force a complete clear & redraw of the screen
force_redraw = ["f"]
################# #################
# Custom Colors # # Custom Colors #
+123 -63
View File
@@ -1,6 +1,6 @@
use std::collections::HashSet; use std::collections::HashSet;
use crossterm::event::KeyCode; use crossterm::event::{KeyCode, KeyModifiers};
/// The macro accepts a list of struct names with key names /// The macro accepts a list of struct names with key names
/// Returns a struct where every key name is an Option<String>, with the correct derived attributes /// Returns a struct where every key name is an Option<String>, with the correct derived attributes
@@ -12,6 +12,7 @@ macro_rules! optional_config_struct {
$( $(
$key_name: Option<Vec<String>>, $key_name: Option<Vec<String>>,
)* )*
pub scroll_many: Option<Vec<String>>,
} }
)* )*
}; };
@@ -27,6 +28,7 @@ macro_rules! config_struct {
$( $(
pub $key_name: (KeyCode, Option<KeyCode>), pub $key_name: (KeyCode, Option<KeyCode>),
)* )*
pub scroll_many: KeyModifiers,
} }
)* )*
}; };
@@ -35,6 +37,7 @@ macro_rules! config_struct {
optional_config_struct!( optional_config_struct!(
ConfigKeymap, ConfigKeymap,
clear, clear,
force_redraw,
delete_deny, delete_deny,
delete_confirm, delete_confirm,
exec, exec,
@@ -42,13 +45,19 @@ optional_config_struct!(
log_section_height_increase, log_section_height_increase,
log_section_height_decrease, log_section_height_decrease,
log_section_toggle, log_section_toggle,
log_scroll_forward,
log_scroll_back,
quit, quit,
save_logs, save_logs,
// TODO remove in next release
scroll_down_many, scroll_down_many,
// TODO rename in next release
scroll_down_one, scroll_down_one,
scroll_end, scroll_end,
scroll_start, scroll_start,
// TODO remove in next release
scroll_up_many, scroll_up_many,
// TODO rename in next release
scroll_up_one, scroll_up_one,
select_next_panel, select_next_panel,
select_previous_panel, select_previous_panel,
@@ -73,16 +82,23 @@ config_struct!(
delete_confirm, delete_confirm,
exec, exec,
filter_mode, filter_mode,
force_redraw,
log_section_height_increase, log_section_height_increase,
log_section_height_decrease, log_section_height_decrease,
log_section_toggle, log_section_toggle,
log_scroll_forward,
log_scroll_back,
quit, quit,
save_logs, save_logs,
// TODO remove in next release
scroll_down_many, scroll_down_many,
// TODO rename in next release
scroll_down_one, scroll_down_one,
scroll_end, scroll_end,
scroll_start, scroll_start,
// TODO remove in next release
scroll_up_many, scroll_up_many,
// TODO rename in next release
scroll_up_one, scroll_up_one,
select_next_panel, select_next_panel,
select_previous_panel, select_previous_panel,
@@ -108,16 +124,24 @@ impl Keymap {
delete_deny: (KeyCode::Char('n'), None), delete_deny: (KeyCode::Char('n'), None),
exec: (KeyCode::Char('e'), None), exec: (KeyCode::Char('e'), None),
filter_mode: (KeyCode::Char('/'), Some(KeyCode::F(1))), filter_mode: (KeyCode::Char('/'), Some(KeyCode::F(1))),
force_redraw: (KeyCode::Char('f'), None),
log_section_height_decrease: (KeyCode::Char('-'), None), log_section_height_decrease: (KeyCode::Char('-'), None),
log_section_height_increase: (KeyCode::Char('='), None), log_section_height_increase: (KeyCode::Char('='), None),
log_section_toggle: (KeyCode::Char('\\'), None), log_section_toggle: (KeyCode::Char('\\'), None),
log_scroll_back: (KeyCode::Left, None),
log_scroll_forward: (KeyCode::Right, None),
quit: (KeyCode::Char('q'), None), quit: (KeyCode::Char('q'), None),
save_logs: (KeyCode::Char('s'), None), save_logs: (KeyCode::Char('s'), None),
// TODO remove in next release
scroll_down_many: (KeyCode::PageDown, None), scroll_down_many: (KeyCode::PageDown, None),
// TODO rename in next release
scroll_down_one: (KeyCode::Down, Some(KeyCode::Char('j'))), scroll_down_one: (KeyCode::Down, Some(KeyCode::Char('j'))),
scroll_end: (KeyCode::End, None), scroll_end: (KeyCode::End, None),
scroll_start: (KeyCode::Home, None), scroll_start: (KeyCode::Home, None),
scroll_many: KeyModifiers::CONTROL,
// TODO remove in next release
scroll_up_many: (KeyCode::PageUp, None), scroll_up_many: (KeyCode::PageUp, None),
// TODO rename in next release
scroll_up_one: (KeyCode::Up, Some(KeyCode::Char('k'))), scroll_up_one: (KeyCode::Up, Some(KeyCode::Char('k'))),
select_next_panel: (KeyCode::Tab, None), select_next_panel: (KeyCode::Tab, None),
select_previous_panel: (KeyCode::BackTab, None), select_previous_panel: (KeyCode::BackTab, None),
@@ -189,6 +213,7 @@ impl From<Option<ConfigKeymap>> for Keymap {
update_keymap(ck.exec, &mut keymap.exec, &mut clash); update_keymap(ck.exec, &mut keymap.exec, &mut clash);
update_keymap(ck.filter_mode, &mut keymap.filter_mode, &mut clash); update_keymap(ck.filter_mode, &mut keymap.filter_mode, &mut clash);
update_keymap(ck.force_redraw, &mut keymap.force_redraw, &mut clash);
update_keymap(ck.quit, &mut keymap.quit, &mut clash); update_keymap(ck.quit, &mut keymap.quit, &mut clash);
update_keymap(ck.save_logs, &mut keymap.save_logs, &mut clash); update_keymap(ck.save_logs, &mut keymap.save_logs, &mut clash);
update_keymap( update_keymap(
@@ -201,6 +226,12 @@ impl From<Option<ConfigKeymap>> for Keymap {
update_keymap(ck.scroll_start, &mut keymap.scroll_start, &mut clash); update_keymap(ck.scroll_start, &mut keymap.scroll_start, &mut clash);
update_keymap(ck.scroll_up_many, &mut keymap.scroll_up_many, &mut clash); update_keymap(ck.scroll_up_many, &mut keymap.scroll_up_many, &mut clash);
update_keymap(ck.scroll_up_one, &mut keymap.scroll_up_one, &mut clash); update_keymap(ck.scroll_up_one, &mut keymap.scroll_up_one, &mut clash);
update_keymap(
ck.log_scroll_forward,
&mut keymap.log_scroll_forward,
&mut clash,
);
update_keymap(ck.log_scroll_back, &mut keymap.log_scroll_back, &mut clash);
update_keymap( update_keymap(
ck.select_next_panel, ck.select_next_panel,
&mut keymap.select_next_panel, &mut keymap.select_next_panel,
@@ -227,6 +258,10 @@ impl From<Option<ConfigKeymap>> for Keymap {
&mut keymap.toggle_mouse_capture, &mut keymap.toggle_mouse_capture,
&mut clash, &mut clash,
); );
// TODO need to check for clashes when using additional modifiers
if let Some(scroll_many) = Self::try_parse_modifier(ck.scroll_many) {
keymap.scroll_many = scroll_many;
}
} }
// A very basic clash check, every key has been inserted into a hashset, and a counter has been increased // A very basic clash check, every key has been inserted into a hashset, and a counter has been increased
// if the counter and hashet length don't match, then there's a clash, and we just return the default keymap // if the counter and hashet length don't match, then there's a clash, and we just return the default keymap
@@ -239,6 +274,20 @@ impl From<Option<ConfigKeymap>> for Keymap {
} }
impl Keymap { impl Keymap {
// Allowable key modifiers are only `shift`, `control`, `alt`
fn try_parse_modifier(input: Option<Vec<String>>) -> Option<KeyModifiers> {
input.and_then(|input| {
input
.first()
.and_then(|input| match input.to_lowercase().trim() {
"control" => Some(KeyModifiers::CONTROL),
"alt" => Some(KeyModifiers::ALT),
"shift" => Some(KeyModifiers::SHIFT),
_ => None,
})
})
}
/// Try to parse a &[String] into a Vec of keycodes, at most the output will have 2 entries /// Try to parse a &[String] into a Vec of keycodes, at most the output will have 2 entries
/// This might fail on MacOS due to Backspace and Delete working in a different manner as to how they work on Linux & Windows /// This might fail on MacOS due to Backspace and Delete working in a different manner as to how they work on Linux & Windows
/// I think that on MacOS `Del` becomes `Fwd Del`, and `Backspace` becomes `Delete` /// I think that on MacOS `Del` becomes `Fwd Del`, and `Backspace` becomes `Delete`
@@ -310,7 +359,7 @@ impl Keymap {
#[cfg(test)] #[cfg(test)]
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
mod tests { mod tests {
use crossterm::event::KeyCode; use crossterm::event::{KeyCode, KeyModifiers};
use crate::config::keymap_parser::ConfigKeymap; use crate::config::keymap_parser::ConfigKeymap;
@@ -364,28 +413,32 @@ mod tests {
delete_deny: Some(vec!["s".to_owned()]), delete_deny: Some(vec!["s".to_owned()]),
delete_confirm: None, delete_confirm: None,
exec: None, exec: None,
filter_mode: None,
force_redraw: None,
log_scroll_back: None,
log_scroll_forward: None,
log_section_height_decrease: None, log_section_height_decrease: None,
log_section_height_increase: None, log_section_height_increase: None,
filter_mode: None, log_section_toggle: None,
quit: None, quit: None,
save_logs: None, save_logs: None,
scroll_down_many: None, scroll_down_many: None,
scroll_down_one: None, scroll_down_one: None,
scroll_end: None, scroll_end: None,
scroll_start: None, scroll_start: None,
scroll_many: None,
scroll_up_many: None, scroll_up_many: None,
scroll_up_one: None, scroll_up_one: None,
select_next_panel: None, select_next_panel: None,
log_section_toggle: None,
select_previous_panel: None, select_previous_panel: None,
sort_by_name: None,
sort_by_state: None,
sort_by_status: None,
sort_by_cpu: None, sort_by_cpu: None,
sort_by_memory: None,
sort_by_id: None, sort_by_id: None,
sort_by_image: None, sort_by_image: None,
sort_by_memory: None,
sort_by_name: None,
sort_by_rx: None, sort_by_rx: None,
sort_by_state: None,
sort_by_status: None,
sort_by_tx: None, sort_by_tx: None,
sort_reset: None, sort_reset: None,
toggle_help: None, toggle_help: None,
@@ -404,72 +457,79 @@ mod tests {
let input = ConfigKeymap { let input = ConfigKeymap {
clear: gen_v(("a", "b")), clear: gen_v(("a", "b")),
delete_confirm: gen_v(("e", "f")), delete_confirm: gen_v(("c", "d")),
delete_deny: gen_v(("c", "d")), delete_deny: gen_v(("e", "fd")),
exec: gen_v(("g", "h")), exec: gen_v(("g", "h")),
filter_mode: gen_v(("i", "j")), filter_mode: gen_v(("i", "j")),
log_section_height_decrease: gen_v(("-", "Z")), force_redraw: gen_v(("k", "l")),
log_section_height_increase: gen_v(("=", "X")), log_section_height_decrease: gen_v(("m", "n")),
log_section_toggle: gen_v(("Y", "W")), log_section_height_increase: gen_v(("o", "p")),
quit: gen_v(("k", "l")), log_scroll_forward: gen_v(("q", "r")),
save_logs: gen_v(("m", "n")), log_scroll_back: gen_v(("s", "t")),
scroll_down_many: gen_v(("o", "p")), log_section_toggle: gen_v(("u", "v")),
scroll_down_one: gen_v(("q", "r")), quit: gen_v(("w", "x")),
scroll_end: gen_v(("s", "t")), save_logs: gen_v(("y", "z")),
scroll_start: gen_v(("u", "v")), scroll_down_many: gen_v(("1", "2")),
scroll_up_many: gen_v(("w", "x")), scroll_down_one: gen_v(("3", "4")),
scroll_up_one: gen_v(("y", "z")), scroll_end: gen_v(("5", "6")),
select_next_panel: gen_v(("0", "1")), scroll_many: Some(vec!["alt".to_owned()]),
select_previous_panel: gen_v(("2", "3")), scroll_start: gen_v(("7", "8")),
sort_by_cpu: gen_v(("F1", "F12")), scroll_up_many: gen_v(("9", "0")),
sort_by_id: gen_v(("[", "]")), scroll_up_one: gen_v(("F1", "F2")),
sort_by_image: gen_v(("A", "B")), select_next_panel: gen_v(("F3", "F4")),
sort_by_memory: gen_v(("/", "\\")), select_previous_panel: gen_v(("F5", "F6")),
sort_by_name: gen_v(("4", "5")), sort_by_cpu: gen_v(("F7", "F8")),
sort_by_rx: gen_v(("C", "D")), sort_by_id: gen_v(("F9", "F10")),
sort_by_state: gen_v(("6", "7")), sort_by_image: gen_v(("F11", "F12")),
sort_by_status: gen_v(("8", "9")), sort_by_memory: gen_v(("HOME", "END")),
sort_by_tx: gen_v(("insert", "TAB")), sort_by_name: gen_v(("UP", "DOWN")),
sort_reset: gen_v(("up", "down")), sort_by_rx: gen_v(("LEFT", "RIGHT")),
toggle_help: gen_v(("home", "end")), sort_by_state: gen_v(("[", "]")),
toggle_mouse_capture: gen_v(("pagedown", "PAGEUP")), sort_by_status: gen_v(("INSERTt", "TAB")),
sort_by_tx: gen_v(("PAGEDOWN", "PAGEUP")),
sort_reset: gen_v((",", ".")),
toggle_help: gen_v(("-", "=")),
toggle_mouse_capture: gen_v(("\\", "/")),
}; };
let result = Keymap::from(Some(input)); let result = Keymap::from(Some(input));
let expected = Keymap { let expected = Keymap {
clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))), clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))),
delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))), delete_deny: (KeyCode::Char('e'), None),
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))), delete_confirm: (KeyCode::Char('c'), Some(KeyCode::Char('d'))),
log_section_height_decrease: (KeyCode::Char('-'), Some(KeyCode::Char('Z'))),
log_section_height_increase: (KeyCode::Char('='), Some(KeyCode::Char('X'))),
log_section_toggle: (KeyCode::Char('Y'), Some(KeyCode::Char('W'))),
exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))), exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))),
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))),
quit: (KeyCode::Char('k'), Some(KeyCode::Char('l'))), force_redraw: (KeyCode::Char('k'), Some(KeyCode::Char('l'))),
save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), log_section_height_increase: (KeyCode::Char('o'), Some(KeyCode::Char('p'))),
scroll_down_many: (KeyCode::Char('o'), Some(KeyCode::Char('p'))), log_section_height_decrease: (KeyCode::Char('m'), Some(KeyCode::Char('n'))),
scroll_down_one: (KeyCode::Char('q'), Some(KeyCode::Char('r'))), log_section_toggle: (KeyCode::Char('u'), Some(KeyCode::Char('v'))),
scroll_end: (KeyCode::Char('s'), Some(KeyCode::Char('t'))), log_scroll_forward: (KeyCode::Char('q'), Some(KeyCode::Char('r'))),
scroll_start: (KeyCode::Char('u'), Some(KeyCode::Char('v'))), log_scroll_back: (KeyCode::Char('s'), Some(KeyCode::Char('t'))),
scroll_up_many: (KeyCode::Char('w'), Some(KeyCode::Char('x'))), quit: (KeyCode::Char('w'), Some(KeyCode::Char('x'))),
scroll_up_one: (KeyCode::Char('y'), Some(KeyCode::Char('z'))), save_logs: (KeyCode::Char('y'), Some(KeyCode::Char('z'))),
select_next_panel: (KeyCode::Char('0'), Some(KeyCode::Char('1'))), scroll_down_many: (KeyCode::Char('1'), Some(KeyCode::Char('2'))),
select_previous_panel: (KeyCode::Char('2'), Some(KeyCode::Char('3'))), scroll_down_one: (KeyCode::Char('3'), Some(KeyCode::Char('4'))),
sort_by_name: (KeyCode::Char('4'), Some(KeyCode::Char('5'))), scroll_end: (KeyCode::Char('5'), Some(KeyCode::Char('6'))),
sort_by_state: (KeyCode::Char('6'), Some(KeyCode::Char('7'))), scroll_start: (KeyCode::Char('7'), Some(KeyCode::Char('8'))),
sort_by_status: (KeyCode::Char('8'), Some(KeyCode::Char('9'))), scroll_up_many: (KeyCode::Char('9'), Some(KeyCode::Char('0'))),
sort_by_cpu: (KeyCode::F(1), Some(KeyCode::F(12))), scroll_up_one: (KeyCode::F(1), Some(KeyCode::F(2))),
sort_by_memory: (KeyCode::Char('/'), Some(KeyCode::Char('\\'))), select_next_panel: (KeyCode::F(3), Some(KeyCode::F(4))),
sort_by_id: (KeyCode::Char('['), Some(KeyCode::Char(']'))), select_previous_panel: (KeyCode::F(5), Some(KeyCode::F(6))),
sort_by_image: (KeyCode::Char('A'), Some(KeyCode::Char('B'))), sort_by_name: (KeyCode::Up, Some(KeyCode::Down)),
sort_by_rx: (KeyCode::Char('C'), Some(KeyCode::Char('D'))), sort_by_state: (KeyCode::Char('['), Some(KeyCode::Char(']'))),
sort_by_tx: (KeyCode::Insert, Some(KeyCode::Tab)), sort_by_status: (KeyCode::Tab, None),
sort_reset: (KeyCode::Up, Some(KeyCode::Down)), sort_by_cpu: (KeyCode::F(7), Some(KeyCode::F(8))),
toggle_help: (KeyCode::Home, Some(KeyCode::End)), sort_by_memory: (KeyCode::Home, Some(KeyCode::End)),
toggle_mouse_capture: (KeyCode::PageDown, Some(KeyCode::PageUp)), sort_by_id: (KeyCode::F(9), Some(KeyCode::F(10))),
sort_by_image: (KeyCode::F(11), Some(KeyCode::F(12))),
sort_by_rx: (KeyCode::Left, Some(KeyCode::Right)),
sort_by_tx: (KeyCode::PageDown, Some(KeyCode::PageUp)),
sort_reset: (KeyCode::Char(','), Some(KeyCode::Char('.'))),
toggle_help: (KeyCode::Char('-'), Some(KeyCode::Char('='))),
toggle_mouse_capture: (KeyCode::Char('\\'), Some(KeyCode::Char('/'))),
scroll_many: KeyModifiers::ALT,
}; };
assert_eq!(expected, result); assert_eq!(expected, result);
} }
} }
+174 -235
View File
@@ -1,9 +1,10 @@
use bollard::{ use bollard::{
Docker, Docker,
container::{ query_parameters::{
ListContainersOptions, LogsOptions, MemoryStatsStats, RemoveContainerOptions, ListContainersOptions, LogsOptions, RemoveContainerOptions, RestartContainerOptions,
StartContainerOptions, Stats, StatsOptions, StartContainerOptions, StatsOptions, StopContainerOptions,
}, },
secret::ContainerStatsResponse,
service::ContainerSummary, service::ContainerSummary,
}; };
use futures_util::StreamExt; use futures_util::StreamExt;
@@ -75,31 +76,44 @@ pub struct DockerData {
impl DockerData { impl DockerData {
/// Use docker stats to calculate current cpu usage /// Use docker stats to calculate current cpu usage
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
fn calculate_usage(stats: &Stats) -> f64 { fn calculate_usage(stats: &ContainerStatsResponse) -> f64 {
let mut cpu_percentage = 0.0; let mut cpu_percentage = 0.0;
let cpu_delta = stats
.cpu_stats
.cpu_usage
.total_usage
.saturating_sub(stats.precpu_stats.cpu_usage.total_usage)
as f64;
if let (Some(cpu_stats_usage), Some(precpu_stats_usage)) = ( let total_usage = stats.precpu_stats.as_ref().map_or(0, |i| {
stats.cpu_stats.system_cpu_usage, i.cpu_usage
stats.precpu_stats.system_cpu_usage, .as_ref()
.map_or(0, |i| i.total_usage.unwrap_or_default())
});
let cpu_delta = stats.cpu_stats.as_ref().map_or(0, |i| {
i.cpu_usage.as_ref().map_or(0, |i| {
i.total_usage
.unwrap_or_default()
.saturating_sub(total_usage)
})
}) as f64;
if let (Some(Some(cpu_stats_usage)), Some(Some(precpu_stats_usage))) = (
stats.cpu_stats.as_ref().map(|i| i.system_cpu_usage),
stats.precpu_stats.as_ref().map(|i| i.system_cpu_usage),
) { ) {
let system_delta = cpu_stats_usage.saturating_sub(precpu_stats_usage) as f64; let system_delta = cpu_stats_usage.saturating_sub(precpu_stats_usage) as f64;
let online_cpus = stats.cpu_stats.online_cpus.unwrap_or_else(|| { let online_cpus = f64::from(stats.cpu_stats.as_ref().map_or(0, |i| {
u64::try_from( i.online_cpus.unwrap_or_else(|| {
u32::try_from(
stats stats
.cpu_stats .cpu_stats
.clone()
.unwrap_or_default()
.cpu_usage .cpu_usage
.unwrap_or_default()
.percpu_usage .percpu_usage
.as_ref() .as_ref()
.map_or(0, std::vec::Vec::len), .map_or(0, std::vec::Vec::len),
) )
.unwrap_or_default() .unwrap_or_default()
}) as f64; })
}));
if system_delta > 0.0 && cpu_delta > 0.0 { if system_delta > 0.0 && cpu_delta > 0.0 {
cpu_percentage = (cpu_delta / system_delta) * online_cpus * 100.0; cpu_percentage = (cpu_delta / system_delta) * online_cpus * 100.0;
} }
@@ -133,18 +147,20 @@ impl DockerData {
while let Some(Ok(stats)) = stream.next().await { while let Some(Ok(stats)) = stream.next().await {
// Memory stats are only collected if the container is alive - is this the behaviour we want? // Memory stats are only collected if the container is alive - is this the behaviour we want?
let (mem_stat, cpu_stats) = if state.is_alive() { let (mem_stat, cpu_stats) = if state.is_alive() {
let mem_cache = stats.memory_stats.stats.map_or(0, |i| match i { let mem_cache = stats.memory_stats.as_ref().map_or(&0, |i| {
MemoryStatsStats::V1(x) => x.inactive_file, i.stats
MemoryStatsStats::V2(x) => x.inactive_file, .as_ref()
.map_or(&0, |i| i.get("inactive_file").unwrap_or(&0))
}); });
( (
Some( Some(
stats stats
.memory_stats .memory_stats
.usage .as_ref()
.unwrap_or_default() .map_or(0, |i| i.usage.unwrap_or_default())
.saturating_sub(mem_cache), .saturating_sub(*mem_cache),
), ),
Some(Self::calculate_usage(&stats)), Some(Self::calculate_usage(&stats)),
) )
@@ -152,26 +168,25 @@ impl DockerData {
(None, None) (None, None)
}; };
let op_key = stats // TODO is hardcoded eth0 a good idea here?
.networks let (rx, tx) = stats.networks.as_ref().map_or((0, 0), |i| {
.as_ref() i.get("eth0").map_or((0, 0), |x| {
.and_then(|networks| networks.keys().next().cloned()); (
x.rx_bytes.unwrap_or_default(),
let (rx, tx) = if let Some(key) = op_key { x.tx_bytes.unwrap_or_default(),
stats )
.networks })
.unwrap_or_default() });
.get(&key)
.map_or((0, 0), |f| (f.rx_bytes, f.tx_bytes))
} else {
(0, 0)
};
app_data.lock().update_stats_by_id( app_data.lock().update_stats_by_id(
id, id,
cpu_stats, cpu_stats,
mem_stat, mem_stat,
stats.memory_stats.limit.unwrap_or_default(), stats
.memory_stats
.unwrap_or_default()
.limit
.unwrap_or_default(),
rx, rx,
tx, tx,
); );
@@ -206,7 +221,7 @@ impl DockerData {
async fn update_all_containers(&self) { async fn update_all_containers(&self) {
let containers = self let containers = self
.docker .docker
.list_containers(Some(ListContainersOptions::<String> { .list_containers(Some(ListContainersOptions {
all: true, all: true,
..Default::default() ..Default::default()
})) }))
@@ -244,11 +259,11 @@ impl DockerData {
spawns: Arc<Mutex<HashMap<SpawnId, JoinHandle<()>>>>, spawns: Arc<Mutex<HashMap<SpawnId, JoinHandle<()>>>>,
stderr: bool, stderr: bool,
) { ) {
let options = Some(LogsOptions::<String> { let options = Some(LogsOptions {
stdout: true, stdout: true,
stderr, stderr,
timestamps: true, timestamps: true,
since: i64::try_from(since).unwrap_or_default(), since: i32::try_from(since).unwrap_or_default(),
..Default::default() ..Default::default()
}); });
@@ -365,14 +380,22 @@ impl DockerData {
.await .await
} }
DockerCommand::Pause => docker.pause_container(id.get()).await, DockerCommand::Pause => docker.pause_container(id.get()).await,
DockerCommand::Restart => docker.restart_container(id.get(), None).await, DockerCommand::Restart => {
docker
.restart_container(id.get(), None::<RestartContainerOptions>)
.await
}
DockerCommand::Resume => docker.unpause_container(id.get()).await, DockerCommand::Resume => docker.unpause_container(id.get()).await,
DockerCommand::Start => { DockerCommand::Start => {
docker docker
.start_container(id.get(), None::<StartContainerOptions<String>>) .start_container(id.get(), None::<StartContainerOptions>)
.await
}
DockerCommand::Stop => {
docker
.stop_container(id.get(), None::<StopContainerOptions>)
.await .await
} }
DockerCommand::Stop => docker.stop_container(id.get(), None).await,
} }
.is_err() .is_err()
{ {
@@ -448,119 +471,72 @@ impl DockerData {
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
mod tests { mod tests {
use bollard::container::{ use bollard::secret::{ContainerCpuStats, ContainerCpuUsage};
BlkioStats, CPUStats, CPUUsage, MemoryStats, PidsStats, Stats, StorageStats, ThrottlingData,
};
use super::*; use super::*;
fn gen_stats() -> Stats { fn gen_stats() -> ContainerStatsResponse {
Stats { ContainerStatsResponse {
read: String::new(), read: None,
preread: String::new(), preread: None,
num_procs: 1, num_procs: Some(1),
pids_stats: PidsStats { pids_stats: None,
current: None,
limit: None,
},
network: None,
networks: None, networks: None,
memory_stats: MemoryStats { memory_stats: None,
stats: None, blkio_stats: None,
max_usage: None, cpu_stats: Some(ContainerCpuStats {
usage: None, cpu_usage: Some(ContainerCpuUsage {
failcnt: None,
limit: None,
commit: None,
commit_peak: None,
commitbytes: None,
commitpeakbytes: None,
privateworkingset: None,
},
blkio_stats: BlkioStats {
io_service_bytes_recursive: None,
io_serviced_recursive: None,
io_queue_recursive: None,
io_service_time_recursive: None,
io_wait_time_recursive: None,
io_merged_recursive: None,
io_time_recursive: None,
sectors_recursive: None,
},
cpu_stats: CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: 10, usage_in_usermode: Some(10),
total_usage: 100, total_usage: Some(100),
usage_in_kernelmode: 20, usage_in_kernelmode: Some(20),
}, }),
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, }),
throttled_periods: 0, precpu_stats: Some(ContainerCpuStats {
throttled_time: 0, cpu_usage: Some(ContainerCpuUsage {
},
},
precpu_stats: CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: 10, usage_in_usermode: Some(10),
total_usage: 100, total_usage: Some(100),
usage_in_kernelmode: 20, usage_in_kernelmode: Some(20),
}, }),
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, }),
throttled_periods: 0, storage_stats: None,
throttled_time: 0, name: None,
}, id: None,
},
storage_stats: StorageStats {
read_count_normalized: None,
read_size_bytes: None,
write_count_normalized: None,
write_size_bytes: None,
},
name: String::new(),
id: String::new(),
} }
} }
#[test] #[test]
fn test_calculate_usage_50() { fn test_calculate_usage_50() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = CPUStats { stats.precpu_stats = Some(ContainerCpuStats {
cpu_usage: CPUUsage { cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: 10, usage_in_usermode: Some(10),
total_usage: 100, total_usage: Some(100),
usage_in_kernelmode: 20, usage_in_kernelmode: Some(20),
}, }),
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0, stats.cpu_stats = Some(ContainerCpuStats {
throttled_time: 0, cpu_usage: Some(ContainerCpuUsage {
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![150]), percpu_usage: Some(vec![150]),
usage_in_usermode: 20, usage_in_usermode: Some(20),
total_usage: 150, total_usage: Some(150),
usage_in_kernelmode: 30, usage_in_kernelmode: Some(30),
}, }),
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(50.0, cpu_percentage); assert_eq!(50.0, cpu_percentage);
} }
@@ -568,37 +544,28 @@ mod tests {
#[test] #[test]
fn test_calculate_usage_25() { fn test_calculate_usage_25() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = CPUStats { stats.precpu_stats = Some(ContainerCpuStats {
cpu_usage: CPUUsage { cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: 10, usage_in_usermode: Some(10),
total_usage: 100, total_usage: Some(100),
usage_in_kernelmode: 20, usage_in_kernelmode: Some(20),
}, }),
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0, stats.cpu_stats = Some(ContainerCpuStats {
throttled_time: 0, cpu_usage: Some(ContainerCpuUsage {
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![75]), percpu_usage: Some(vec![75]),
usage_in_usermode: 20, usage_in_usermode: Some(20),
total_usage: 125, total_usage: Some(125),
usage_in_kernelmode: 30, usage_in_kernelmode: Some(30),
}, }),
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(25.0, cpu_percentage); assert_eq!(25.0, cpu_percentage);
} }
@@ -606,38 +573,28 @@ mod tests {
#[test] #[test]
fn test_calculate_usage_75() { fn test_calculate_usage_75() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = CPUStats { stats.precpu_stats = Some(ContainerCpuStats {
cpu_usage: CPUUsage { cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: 10, usage_in_usermode: Some(10),
total_usage: 100, total_usage: Some(100),
usage_in_kernelmode: 20, usage_in_kernelmode: Some(20),
}, }),
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0, stats.cpu_stats = Some(ContainerCpuStats {
throttled_time: 0, cpu_usage: Some(ContainerCpuUsage {
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![175]), percpu_usage: Some(vec![175]),
usage_in_usermode: 20, usage_in_usermode: Some(20),
total_usage: 175, total_usage: Some(175),
usage_in_kernelmode: 30, usage_in_kernelmode: Some(30),
}, }),
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(75.0, cpu_percentage); assert_eq!(75.0, cpu_percentage);
} }
@@ -645,36 +602,28 @@ mod tests {
#[test] #[test]
fn test_calculate_usage_100() { fn test_calculate_usage_100() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = CPUStats { stats.precpu_stats = Some(ContainerCpuStats {
cpu_usage: CPUUsage { cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: 10, usage_in_usermode: Some(10),
total_usage: 100, total_usage: Some(100),
usage_in_kernelmode: 20, usage_in_kernelmode: Some(20),
}, }),
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0, stats.cpu_stats = Some(ContainerCpuStats {
throttled_time: 0, cpu_usage: Some(ContainerCpuUsage {
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![200]), percpu_usage: Some(vec![200]),
usage_in_usermode: 20, usage_in_usermode: Some(20),
total_usage: 200, total_usage: Some(200),
usage_in_kernelmode: 30, usage_in_kernelmode: Some(30),
}, }),
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(100.0, cpu_percentage); assert_eq!(100.0, cpu_percentage);
} }
@@ -682,38 +631,28 @@ mod tests {
#[test] #[test]
fn test_calculate_usage_175() { fn test_calculate_usage_175() {
let mut stats = gen_stats(); let mut stats = gen_stats();
stats.precpu_stats = CPUStats { stats.precpu_stats = Some(ContainerCpuStats {
cpu_usage: CPUUsage { cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]), percpu_usage: Some(vec![50]),
usage_in_usermode: 10, usage_in_usermode: Some(10),
total_usage: 100, total_usage: Some(100),
usage_in_kernelmode: 20, usage_in_kernelmode: Some(20),
}, }),
system_cpu_usage: Some(400), system_cpu_usage: Some(400),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0, stats.cpu_stats = Some(ContainerCpuStats {
throttled_time: 0, cpu_usage: Some(ContainerCpuUsage {
},
};
stats.cpu_stats = CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![275]), percpu_usage: Some(vec![275]),
usage_in_usermode: 20, usage_in_usermode: Some(20),
total_usage: 275, total_usage: Some(275),
usage_in_kernelmode: 30, usage_in_kernelmode: Some(30),
}, }),
system_cpu_usage: Some(500), system_cpu_usage: Some(500),
online_cpus: Some(1), online_cpus: Some(1),
throttling_data: ThrottlingData { throttling_data: None,
periods: 0, });
throttled_periods: 0,
throttled_time: 0,
},
};
let cpu_percentage = DockerData::calculate_usage(&stats); let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(175.0, cpu_percentage); assert_eq!(175.0, cpu_percentage);
} }
+1 -1
View File
@@ -3,5 +3,5 @@ use crossterm::event::{KeyCode, KeyModifiers, MouseEvent};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum InputMessages { pub enum InputMessages {
ButtonPress((KeyCode, KeyModifiers)), ButtonPress((KeyCode, KeyModifiers)),
MouseEvent(MouseEvent), MouseEvent((MouseEvent, KeyModifiers)),
} }
+83 -20
View File
@@ -5,8 +5,7 @@ use std::{
time::SystemTime, time::SystemTime,
}; };
use bollard::container::LogsOptions; use bollard::query_parameters::LogsOptions;
// use bollard::container::LogsOptions;
use cansi::v3::categorise_text; use cansi::v3::categorise_text;
use crossterm::{ use crossterm::{
event::{DisableMouseCapture, KeyCode, KeyModifiers, MouseButton, MouseEvent, MouseEventKind}, event::{DisableMouseCapture, KeyCode, KeyModifiers, MouseButton, MouseEvent, MouseEventKind},
@@ -68,7 +67,7 @@ impl InputHandler {
while let Some(message) = self.rx.recv().await { while let Some(message) = self.rx.recv().await {
match message { match message {
InputMessages::ButtonPress(key) => self.button_press(key.0, key.1).await, InputMessages::ButtonPress(key) => self.button_press(key.0, key.1).await,
InputMessages::MouseEvent(mouse_event) => { InputMessages::MouseEvent((mouse_event, modifider)) => {
let status = self.gui_state.lock().get_status(); let status = self.gui_state.lock().get_status();
let contains = |s: Status| status.contains(&s); let contains = |s: Status| status.contains(&s);
@@ -79,7 +78,7 @@ impl InputHandler {
| !contains(Status::DeleteConfirm) | !contains(Status::DeleteConfirm)
| !contains(Status::Filter) | !contains(Status::Filter)
{ {
self.mouse_press(mouse_event); self.mouse_press(mouse_event, modifider);
} }
} }
} }
@@ -188,7 +187,7 @@ impl InputHandler {
let path = log_path.join(format!("{name}_{now}.log")); let path = log_path.join(format!("{name}_{now}.log"));
let options = Some(LogsOptions::<String> { let options = Some(LogsOptions {
stderr: true, stderr: true,
stdout: true, stdout: true,
timestamps: args.show_timestamp, timestamps: args.show_timestamp,
@@ -286,6 +285,36 @@ impl InputHandler {
} }
} }
/// If keymap.scroll_modifier is pressed, return 10, else return 1, to speed up scrolling
fn get_modifier_total(&self, modifier: KeyModifiers) -> u8 {
if modifier == self.keymap.scroll_many {
10
} else {
1
}
}
/// Advance the "cursor" along the logs
fn logs_forward(&self, modifier: KeyModifiers) {
let panel = self.gui_state.lock().get_selected_panel();
if panel == SelectablePanel::Logs {
for _ in 0..self.get_modifier_total(modifier) {
let width = self.gui_state.lock().get_screen_width();
self.app_data.lock().log_forward(width);
}
}
}
/// Retreat the "cursor" along the logs
fn logs_back(&self, modifier: KeyModifiers) {
let panel = self.gui_state.lock().get_selected_panel();
if panel == SelectablePanel::Logs {
for _ in 0..self.get_modifier_total(modifier) {
self.app_data.lock().log_back();
}
}
}
/// Change the the "next" selectable panel /// Change the the "next" selectable panel
/// If no containers, and on Commands panel, skip to next panel, as Commands panel isn't visible in this state /// If no containers, and on Commands panel, skip to next panel, as Commands panel isn't visible in this state
fn next_panel_key(&self) { fn next_panel_key(&self) {
@@ -391,6 +420,11 @@ impl InputHandler {
/// Handle input that refers to the sorting of columns /// Handle input that refers to the sorting of columns
fn handle_sort(&self, key_code: KeyCode) { fn handle_sort(&self, key_code: KeyCode) {
match key_code { match key_code {
_ if self.keymap.force_redraw.0 == key_code
|| self.keymap.force_redraw.1 == Some(key_code) =>
{
self.gui_state.lock().set_clear();
}
_ if self.keymap.sort_reset.0 == key_code _ if self.keymap.sort_reset.0 == key_code
|| self.keymap.sort_reset.1 == Some(key_code) => || self.keymap.sort_reset.1 == Some(key_code) =>
{ {
@@ -467,7 +501,8 @@ impl InputHandler {
} }
/// Handle button presses in all other scenarios /// Handle button presses in all other scenarios
async fn handle_others(&mut self, key_code: KeyCode) { #[allow(clippy::cognitive_complexity)]
async fn handle_others(&mut self, key_code: KeyCode, modifier: KeyModifiers) {
self.handle_sort(key_code); self.handle_sort(key_code);
// shift key plus arrows // shift key plus arrows
match key_code { match key_code {
@@ -537,28 +572,28 @@ impl InputHandler {
_ if self.keymap.scroll_up_one.0 == key_code _ if self.keymap.scroll_up_one.0 == key_code
|| self.keymap.scroll_up_one.1 == Some(key_code) => || self.keymap.scroll_up_one.1 == Some(key_code) =>
{ {
self.previous(); self.scroll_up(modifier);
} }
_ if self.keymap.scroll_up_many.0 == key_code _ if self.keymap.scroll_up_many.0 == key_code
|| self.keymap.scroll_up_many.1 == Some(key_code) => || self.keymap.scroll_up_many.1 == Some(key_code) =>
{ {
for _ in 0..=6 { for _ in 0..=6 {
self.previous(); self.scroll_up(modifier);
} }
} }
_ if self.keymap.scroll_down_one.0 == key_code _ if self.keymap.scroll_down_one.0 == key_code
|| self.keymap.scroll_down_one.1 == Some(key_code) => || self.keymap.scroll_down_one.1 == Some(key_code) =>
{ {
self.next(); self.scroll_down(modifier);
} }
_ if self.keymap.scroll_down_many.0 == key_code _ if self.keymap.scroll_down_many.0 == key_code
|| self.keymap.scroll_down_many.1 == Some(key_code) => || self.keymap.scroll_down_many.1 == Some(key_code) =>
{ {
for _ in 0..=6 { for _ in 0..=6 {
self.next(); self.scroll_down(modifier);
} }
} }
@@ -569,6 +604,18 @@ impl InputHandler {
self.docker_tx.send(DockerMessage::Update).await.ok(); self.docker_tx.send(DockerMessage::Update).await.ok();
} }
_ if self.keymap.log_scroll_back.0 == key_code
|| self.keymap.log_scroll_back.1 == Some(key_code) =>
{
self.logs_back(modifier);
}
_ if self.keymap.log_scroll_forward.0 == key_code
|| self.keymap.log_scroll_forward.1 == Some(key_code) =>
{
self.logs_forward(modifier);
}
KeyCode::Enter => self.enter_key().await, KeyCode::Enter => self.enter_key().await,
_ => (), _ => (),
} }
@@ -603,7 +650,7 @@ impl InputHandler {
} else if contains_delete { } else if contains_delete {
self.handle_delete(key_code).await; self.handle_delete(key_code).await;
} else { } else {
self.handle_others(key_code).await; self.handle_others(key_code, key_modifier).await;
} }
} }
} }
@@ -628,7 +675,7 @@ impl InputHandler {
} }
/// Handle mouse button events /// Handle mouse button events
fn mouse_press(&self, mouse_event: MouseEvent) { fn mouse_press(&self, mouse_event: MouseEvent, modifier: KeyModifiers) {
let status = self.gui_state.lock().get_status(); let status = self.gui_state.lock().get_status();
if status.contains(&Status::Help) { if status.contains(&Status::Help) {
let mouse_point = Rect::new(mouse_event.column, mouse_event.row, 1, 1); let mouse_point = Rect::new(mouse_event.column, mouse_event.row, 1, 1);
@@ -638,8 +685,8 @@ impl InputHandler {
} }
} else { } else {
match mouse_event.kind { match mouse_event.kind {
MouseEventKind::ScrollUp => self.previous(), MouseEventKind::ScrollUp => self.scroll_up(modifier),
MouseEventKind::ScrollDown => self.next(), MouseEventKind::ScrollDown => self.scroll_down(modifier),
MouseEventKind::Down(MouseButton::Left) => { MouseEventKind::Down(MouseButton::Left) => {
let mouse_point = Rect::new(mouse_event.column, mouse_event.row, 1, 1); let mouse_point = Rect::new(mouse_event.column, mouse_event.row, 1, 1);
let header = self.gui_state.lock().get_intersect_header(mouse_point); let header = self.gui_state.lock().get_intersect_header(mouse_point);
@@ -659,21 +706,37 @@ impl InputHandler {
} }
/// Change state to next, depending which panel is currently in focus /// Change state to next, depending which panel is currently in focus
fn next(&self) { fn scroll_down(&self, modifier: KeyModifiers) {
let selected_panel = self.gui_state.lock().get_selected_panel(); let selected_panel = self.gui_state.lock().get_selected_panel();
match selected_panel { match selected_panel {
SelectablePanel::Containers => self.app_data.lock().containers_next(), SelectablePanel::Containers => {
SelectablePanel::Logs => self.app_data.lock().log_next(), for _ in 0..self.get_modifier_total(modifier) {
self.app_data.lock().containers_next();
}
}
SelectablePanel::Logs => {
for _ in 0..self.get_modifier_total(modifier) {
self.app_data.lock().log_next();
}
}
SelectablePanel::Commands => self.app_data.lock().docker_controls_next(), SelectablePanel::Commands => self.app_data.lock().docker_controls_next(),
} }
} }
/// Change state to previous, depending which panel is currently in focus /// Change state to previous, depending which panel is currently in focus
fn previous(&self) { fn scroll_up(&self, modifier: KeyModifiers) {
let selected_panel = self.gui_state.lock().get_selected_panel(); let selected_panel = self.gui_state.lock().get_selected_panel();
match selected_panel { match selected_panel {
SelectablePanel::Containers => self.app_data.lock().containers_previous(), SelectablePanel::Containers => {
SelectablePanel::Logs => self.app_data.lock().log_previous(), for _ in 0..self.get_modifier_total(modifier) {
self.app_data.lock().containers_previous();
}
}
SelectablePanel::Logs => {
for _ in 0..self.get_modifier_total(modifier) {
self.app_data.lock().log_previous();
}
}
SelectablePanel::Commands => self.app_data.lock().docker_controls_previous(), SelectablePanel::Commands => self.app_data.lock().docker_controls_previous(),
} }
} }
+7 -3
View File
@@ -1,3 +1,6 @@
#![allow(clippy::collapsible_if)]
// Zigbuild is stuck on 1.87.0, which means Mac builds won't work when using collapsible ifs
use app_data::AppData; use app_data::AppData;
use app_error::AppError; use app_error::AppError;
use bollard::{API_DEFAULT_VERSION, Docker}; use bollard::{API_DEFAULT_VERSION, Docker};
@@ -149,7 +152,7 @@ async fn main() {
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
mod tests { mod tests {
use std::sync::Arc; use std::{str::FromStr, sync::Arc};
use bollard::service::{ContainerSummary, Port}; use bollard::service::{ContainerSummary, Port};
@@ -208,7 +211,7 @@ mod tests {
current_sorted_id: vec![], current_sorted_id: vec![],
error: None, error: None,
sorted_by: None, sorted_by: None,
redraw: Arc::new(Rerender::new()), rerender: Arc::new(Rerender::new()),
filter: Filter::new(), filter: Filter::new(),
config: gen_config(), config: gen_config(),
} }
@@ -228,6 +231,7 @@ mod tests {
pub fn gen_container_summary(index: usize, state: &str) -> ContainerSummary { pub fn gen_container_summary(index: usize, state: &str) -> ContainerSummary {
ContainerSummary { ContainerSummary {
image_manifest_descriptor: None,
id: Some(format!("{index}")), id: Some(format!("{index}")),
names: Some(vec![format!("container_{}", index)]), names: Some(vec![format!("container_{}", index)]),
image: Some(format!("image_{index}")), image: Some(format!("image_{index}")),
@@ -243,7 +247,7 @@ mod tests {
size_rw: None, size_rw: None,
size_root_fs: None, size_root_fs: None,
labels: None, labels: None,
state: Some(state.to_owned()), state: Some(bollard::secret::ContainerSummaryStateEnum::from_str(state).unwrap()),
status: Some(format!("Up {index} hour")), status: Some(format!("Up {index} hour")),
host_config: None, host_config: None,
network_settings: None, network_settings: None,
+1 -1
View File
@@ -8,7 +8,7 @@ use ratatui::{
use crate::{app_data::FilterBy, config::AppColors, ui::FrameData}; use crate::{app_data::FilterBy, config::AppColors, ui::FrameData};
/// Create the filter_by by spans, coloured dependant on which one is selected /// Create the filter_by by spans, coloured dependant on which one is selected
fn filter_by_spans(colors: AppColors, fd: &FrameData) -> [Span; 4] { fn filter_by_spans(colors: AppColors, fd: &'_ FrameData) -> [Span<'_>; 4] {
let selected = Style::default() let selected = Style::default()
.bg(colors.filter.selected_filter_background) .bg(colors.filter.selected_filter_background)
.fg(colors.filter.selected_filter_text); .fg(colors.filter.selected_filter_text);
+164 -116
View File
@@ -109,7 +109,17 @@ impl HelpInfo {
button_item("PgUp PgDown"), button_item("PgUp PgDown"),
or(), or(),
button_item("Home End"), button_item("Home End"),
button_desc("change selected line"), button_desc("scroll vertically"),
]),
Line::from(vec![
space(),
button_item("← →"),
button_desc("horizontal scroll across logs"),
]),
Line::from(vec![
space(),
button_item("ctrl"),
button_desc("increase scroll speed, used in conjuction scroll keys"),
]), ]),
Line::from(vec![ Line::from(vec![
space(), space(),
@@ -123,6 +133,11 @@ impl HelpInfo {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
button_desc(" - not available on Windows"), button_desc(" - not available on Windows"),
]), ]),
Line::from(vec![
space(),
button_item("f"),
button_desc("force clear the screen & redraw the gui"),
]),
Line::from(vec![ Line::from(vec![
space(), space(),
button_item("h"), button_item("h"),
@@ -268,6 +283,13 @@ impl HelpInfo {
or_secondary(km.scroll_up_many, "scroll list by up many"), or_secondary(km.scroll_up_many, "scroll list by up many"),
or_secondary(km.scroll_end, "scroll list to end"), or_secondary(km.scroll_end, "scroll list to end"),
or_secondary(km.scroll_start, "scroll list to start"), or_secondary(km.scroll_start, "scroll list to start"),
or_secondary(km.log_scroll_forward, "horizontal scroll logs right"),
or_secondary(km.log_scroll_back, "horizontal scroll logs left"),
Line::from(vec![
space(),
button_item(km.scroll_many.to_string().as_str()),
button_desc("increase scroll speed, used in conjuction scroll keys"),
]),
Line::from(vec![ Line::from(vec![
space(), space(),
button_item("enter"), button_item("enter"),
@@ -277,6 +299,7 @@ impl HelpInfo {
or_secondary(km.exec, "exec into a container"), or_secondary(km.exec, "exec into a container"),
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
or_secondary(km.exec, "exec into a container - not available on Windows"), or_secondary(km.exec, "exec into a container - not available on Windows"),
or_secondary(km.force_redraw, "force clear the screen & redraw the gui"),
or_secondary( or_secondary(
km.toggle_help, km.toggle_help,
"toggle this help information - or click heading", "toggle this help information - or click heading",
@@ -427,7 +450,7 @@ pub fn draw(
#[allow(clippy::unwrap_used, clippy::too_many_lines)] #[allow(clippy::unwrap_used, clippy::too_many_lines)]
mod tests { mod tests {
use crate::config::{AppColors, Keymap}; use crate::config::{AppColors, Keymap};
use crossterm::event::KeyCode; use crossterm::event::{KeyCode, KeyModifiers};
use insta::assert_snapshot; use insta::assert_snapshot;
use jiff::tz::TimeZone; use jiff::tz::TimeZone;
use ratatui::style::{Color, Modifier}; use ratatui::style::{Color, Modifier};
@@ -436,8 +459,10 @@ mod tests {
#[test] #[test]
/// This will cause issues once the version has more than the current 5 chars (0.5.0) /// This will cause issues once the version has more than the current 5 chars (0.5.0)
/// This test is incredibly annoying
/// println!("{} {} {} {} {}", row_index, result_cell_index, result_cell.symbol(), result_cell.bg, result_cell.fg);
fn test_draw_blocks_help() { fn test_draw_blocks_help() {
let mut setup = test_setup(87, 35, true, true); let mut setup = test_setup(87, 37, true, true);
let tz = setup.app_data.lock().config.timezone.clone(); let tz = setup.app_data.lock().config.timezone.clone();
setup setup
@@ -457,36 +482,45 @@ mod tests {
for (row_index, result_row) in get_result(&setup) { for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() { for (result_cell_index, result_cell) in result_row.iter().enumerate() {
println!(
"{} {} {} {} {}",
row_index,
result_cell_index,
result_cell.symbol(),
result_cell.bg,
result_cell.fg
);
match (row_index, result_cell_index) { match (row_index, result_cell_index) {
// first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area // first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area
(0 | 34, _) | (0..=33, 0 | 86) => { (0 | 36, _) | (0..=35, 0 | 86) => {
assert_eq!(result_cell.bg, Color::Reset); assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Reset); assert_eq!(result_cell.fg, Color::Reset);
} }
// border is black on magenta // border is red on black
(1 | 32, _) | (1..=31, 1 | 85) => { (1 | 34, _) | (1..=31, 1 | 85) => {
assert_eq!(result_cell.bg, Color::Magenta); assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::Black); assert_eq!(result_cell.fg, Color::Black);
} }
// oxker logo && description // Buttons
(2..=10, 2..=85) (2..=10, 2..=85)
| (12, 19..=66) | (12, 19..=66)
| (14, 2..=10 | 13..=27) | (14, 2..=10 | 13..=27)
| (15, 2..=10 | 13..=21 | 24..=40 | 43..=56) | (15, 2..=10 | 13..=21 | 24..=40 | 43..=56)
| (16 | 23, 2..=12) | (16 | 27 | 29, 2..=10)
| (17..=20 | 22 | 25 | 27, 2..=8) | (17, 2..=11)
| (21, 2..=9 | 12..=18) | (18 | 26, 2..=12)
| (24 | 26, 2..=10) => { | (19 | 20 | 21 | 22 | 24 | 25 | 28 | 23 | 30, 2..=8)
| (24, 2..=9 | 12..=18) => {
assert_eq!(result_cell.bg, Color::Magenta); assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::White); assert_eq!(result_cell.fg, Color::White);
} }
// The URL is white and underlined // The URL is yellow and underlined
(30, 25..=60) => { (33, 25..=60) => {
assert_eq!(result_cell.bg, Color::Magenta); assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::White); assert_eq!(result_cell.fg, Color::White);
assert_eq!(result_cell.modifier, Modifier::UNDERLINED); assert_eq!(result_cell.modifier, Modifier::UNDERLINED);
} }
// The rest is black on magenta // The rest is red on black
_ => { _ => {
assert_eq!(result_cell.bg, Color::Magenta); assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::Black); assert_eq!(result_cell.fg, Color::Black);
@@ -498,8 +532,10 @@ mod tests {
#[test] #[test]
/// Test that the help panel gets drawn with custom colors /// Test that the help panel gets drawn with custom colors
/// This test is incredibly annoying
/// println!("{} {} {} {} {}", row_index, result_cell_index, result_cell.symbol(), result_cell.bg, result_cell.fg);
fn test_draw_blocks_help_custom_colors() { fn test_draw_blocks_help_custom_colors() {
let mut setup = test_setup(87, 35, true, true); let mut setup = test_setup(87, 37, true, true);
let mut colors = AppColors::new(); let mut colors = AppColors::new();
let tz = setup.app_data.lock().config.timezone.clone(); let tz = setup.app_data.lock().config.timezone.clone();
@@ -521,34 +557,34 @@ mod tests {
.unwrap(); .unwrap();
assert_snapshot!(setup.terminal.backend()); assert_snapshot!(setup.terminal.backend());
for (row_index, result_row) in get_result(&setup) { for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() { for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) { match (row_index, result_cell_index) {
// first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area // first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area
(0 | 34, _) | (0..=33, 0 | 86) => { (0 | 36, _) | (0..=35, 0 | 86) => {
assert_eq!(result_cell.bg, Color::Reset); assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Reset); assert_eq!(result_cell.fg, Color::Reset);
} }
// border is red on black // border is red on black
(1 | 32, _) | (1..=31, 1 | 85) => { (1 | 34, _) | (1..=31, 1 | 85) => {
assert_eq!(result_cell.bg, Color::Black); assert_eq!(result_cell.bg, Color::Black);
assert_eq!(result_cell.fg, Color::Red); assert_eq!(result_cell.fg, Color::Red);
} }
// oxker logo && description // Buttons
(2..=10, 2..=85) (2..=10, 2..=85)
| (12, 19..=66) | (12, 19..=66)
| (14, 2..=10 | 13..=27) | (14, 2..=10 | 13..=27)
| (15, 2..=10 | 13..=21 | 24..=40 | 43..=56) | (15, 2..=10 | 13..=21 | 24..=40 | 43..=56)
| (16 | 23, 2..=12) | (16 | 27 | 29, 2..=10)
| (17..=20 | 22 | 25 | 27, 2..=8) | (17, 2..=11)
| (21, 2..=9 | 12..=18) | (18 | 26, 2..=12)
| (24 | 26, 2..=10) => { | (19 | 20 | 21 | 22 | 24 | 25 | 28 | 23 | 30, 2..=8)
| (24, 2..=9 | 12..=18) => {
assert_eq!(result_cell.bg, Color::Black); assert_eq!(result_cell.bg, Color::Black);
assert_eq!(result_cell.fg, Color::Yellow); assert_eq!(result_cell.fg, Color::Yellow);
} }
// The URL is yellow and underlined // The URL is yellow and underlined
(30, 25..=60) => { (33, 25..=60) => {
assert_eq!(result_cell.bg, Color::Black); assert_eq!(result_cell.bg, Color::Black);
assert_eq!(result_cell.fg, Color::Yellow); assert_eq!(result_cell.fg, Color::Yellow);
assert_eq!(result_cell.modifier, Modifier::UNDERLINED); assert_eq!(result_cell.modifier, Modifier::UNDERLINED);
@@ -566,39 +602,43 @@ mod tests {
#[test] #[test]
/// Help panel will show custom keymap if in use, with one definition for each entry /// Help panel will show custom keymap if in use, with one definition for each entry
fn test_draw_blocks_help_custom_keymap_one_definition() { fn test_draw_blocks_help_custom_keymap_one_definition() {
let mut setup = test_setup(98, 47, true, true); let mut setup = test_setup(98, 50, true, true);
let input = Keymap { let input = Keymap {
clear: (KeyCode::Char('a'), None), clear: (KeyCode::Char('a'), None),
delete_confirm: (KeyCode::Char('b'), None),
delete_deny: (KeyCode::Char('c'), None), delete_deny: (KeyCode::Char('c'), None),
delete_confirm: (KeyCode::Char('e'), None), exec: (KeyCode::Char('d'), None),
exec: (KeyCode::Char('g'), None), filter_mode: (KeyCode::Char('e'), None),
log_section_height_decrease: (KeyCode::Char('z'), None), force_redraw: (KeyCode::Char('f'), None),
log_section_height_increase: (KeyCode::Char('x'), None), log_scroll_back: (KeyCode::Char('g'), None),
log_section_toggle: (KeyCode::Char('W'), None), log_scroll_forward: (KeyCode::Char('h'), None),
filter_mode: (KeyCode::Char('i'), None), log_section_height_decrease: (KeyCode::Char('i'), None),
quit: (KeyCode::Char('k'), None), log_section_height_increase: (KeyCode::Char('j'), None),
log_section_toggle: (KeyCode::Char('k'), None),
quit: (KeyCode::Char('l'), None),
save_logs: (KeyCode::Char('m'), None), save_logs: (KeyCode::Char('m'), None),
scroll_down_many: (KeyCode::Char('o'), None), scroll_down_many: (KeyCode::Char('n'), None),
scroll_down_one: (KeyCode::Char('q'), None), scroll_down_one: (KeyCode::Char('o'), None),
scroll_end: (KeyCode::Char('s'), None), scroll_end: (KeyCode::Char('p'), None),
scroll_start: (KeyCode::Char('u'), None), scroll_many: KeyModifiers::ALT,
scroll_up_many: (KeyCode::Char('w'), None), scroll_start: (KeyCode::Char('q'), None),
scroll_up_one: (KeyCode::Char('y'), None), scroll_up_many: (KeyCode::Char('r'), None),
select_next_panel: (KeyCode::Char('0'), None), scroll_up_one: (KeyCode::Char('s'), None),
select_previous_panel: (KeyCode::Char('2'), None), select_next_panel: (KeyCode::Char('t'), None),
sort_by_name: (KeyCode::Char('4'), None), select_previous_panel: (KeyCode::Char('u'), None),
sort_by_state: (KeyCode::Char('6'), None), sort_by_cpu: (KeyCode::Char('v'), None),
sort_by_status: (KeyCode::Char('8'), None), sort_by_id: (KeyCode::Char('w'), None),
sort_by_cpu: (KeyCode::F(1), None), sort_by_image: (KeyCode::Char('x'), None),
sort_by_memory: (KeyCode::Char('#'), None), sort_by_memory: (KeyCode::Char('y'), None),
sort_by_id: (KeyCode::Char('/'), None), sort_by_name: (KeyCode::Char('z'), None),
sort_by_image: (KeyCode::Char(','), None), sort_by_rx: (KeyCode::Char('0'), None),
sort_by_rx: (KeyCode::Char('.'), None), sort_by_state: (KeyCode::Char('1'), None),
sort_by_tx: (KeyCode::Insert, None), sort_by_status: (KeyCode::Char('2'), None),
sort_reset: (KeyCode::Up, None), sort_by_tx: (KeyCode::Char('3'), None),
toggle_help: (KeyCode::Home, None), sort_reset: (KeyCode::Char('4'), None),
toggle_mouse_capture: (KeyCode::PageDown, None), toggle_help: (KeyCode::Char('5'), None),
toggle_mouse_capture: (KeyCode::Char('6'), None),
}; };
setup setup
@@ -614,39 +654,43 @@ mod tests {
#[test] #[test]
/// Help panel will show custom keymap if in use, with two definition for each entry /// Help panel will show custom keymap if in use, with two definition for each entry
fn test_draw_blocks_help_custom_keymap_two_definitions() { fn test_draw_blocks_help_custom_keymap_two_definitions() {
let mut setup = test_setup(110, 47, true, true); let mut setup = test_setup(110, 50, true, true);
let keymap = Keymap { let keymap = Keymap {
clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))), clear: (KeyCode::Char('a'), Some(KeyCode::Char('A'))),
delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))), delete_confirm: (KeyCode::Char('b'), Some(KeyCode::Char('B'))),
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))), delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('C'))),
exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))), exec: (KeyCode::Char('d'), Some(KeyCode::Char('D'))),
log_section_height_decrease: (KeyCode::Char('A'), Some(KeyCode::Char('Z'))), filter_mode: (KeyCode::Char('e'), Some(KeyCode::Char('E'))),
log_section_height_increase: (KeyCode::Char('B'), Some(KeyCode::Char('X'))), force_redraw: (KeyCode::Char('f'), Some(KeyCode::Char('F'))),
log_section_toggle: (KeyCode::Char('C'), Some(KeyCode::Char('W'))), log_scroll_back: (KeyCode::Char('f'), Some(KeyCode::Char('F'))),
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), log_scroll_forward: (KeyCode::Char('g'), Some(KeyCode::Char('G'))),
quit: (KeyCode::Char('k'), Some(KeyCode::Char('l'))), log_section_height_decrease: (KeyCode::Char('h'), Some(KeyCode::Char('H'))),
save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), log_section_height_increase: (KeyCode::Char('i'), Some(KeyCode::Char('I'))),
scroll_down_many: (KeyCode::Char('o'), Some(KeyCode::Char('p'))), log_section_toggle: (KeyCode::Char('j'), Some(KeyCode::Char('J'))),
scroll_down_one: (KeyCode::Char('q'), Some(KeyCode::Char('r'))), quit: (KeyCode::Char('k'), Some(KeyCode::Char('K'))),
scroll_end: (KeyCode::Char('s'), Some(KeyCode::Char('t'))), save_logs: (KeyCode::Char('l'), Some(KeyCode::Char('L'))),
scroll_start: (KeyCode::Char('u'), Some(KeyCode::Char('v'))), scroll_down_many: (KeyCode::Char('m'), Some(KeyCode::Char('M'))),
scroll_up_many: (KeyCode::Char('w'), Some(KeyCode::Char('x'))), scroll_down_one: (KeyCode::Char('n'), Some(KeyCode::Char('N'))),
scroll_up_one: (KeyCode::Char('y'), Some(KeyCode::Char('z'))), scroll_end: (KeyCode::Char('o'), Some(KeyCode::Char('O'))),
select_next_panel: (KeyCode::Char('0'), Some(KeyCode::Char('1'))), scroll_many: KeyModifiers::ALT,
select_previous_panel: (KeyCode::Char('2'), Some(KeyCode::Char('3'))), scroll_start: (KeyCode::Char('p'), Some(KeyCode::Char('P'))),
sort_by_name: (KeyCode::Char('4'), Some(KeyCode::Char('5'))), scroll_up_many: (KeyCode::Char('q'), Some(KeyCode::Char('Q'))),
sort_by_state: (KeyCode::Char('6'), Some(KeyCode::Char('7'))), scroll_up_one: (KeyCode::Char('r'), Some(KeyCode::Char('R'))),
sort_by_status: (KeyCode::Char('8'), Some(KeyCode::Char('9'))), select_next_panel: (KeyCode::Char('s'), Some(KeyCode::Char('S'))),
sort_by_cpu: (KeyCode::F(1), Some(KeyCode::F(12))), select_previous_panel: (KeyCode::Char('t'), Some(KeyCode::Char('T'))),
sort_by_memory: (KeyCode::Char('#'), Some(KeyCode::Char('-'))), sort_by_cpu: (KeyCode::Char('u'), Some(KeyCode::Char('U'))),
sort_by_id: (KeyCode::Char('/'), Some(KeyCode::Char('='))), sort_by_id: (KeyCode::Char('v'), Some(KeyCode::Char('V'))),
sort_by_image: (KeyCode::Char(','), Some(KeyCode::Char('\\'))), sort_by_image: (KeyCode::Char('w'), Some(KeyCode::Char('W'))),
sort_by_rx: (KeyCode::Char('.'), Some(KeyCode::Char(']'))), sort_by_memory: (KeyCode::Char('x'), Some(KeyCode::Char('X'))),
sort_by_tx: (KeyCode::Insert, Some(KeyCode::BackTab)), sort_by_name: (KeyCode::Char('y'), Some(KeyCode::Char('Y'))),
sort_reset: (KeyCode::Up, Some(KeyCode::Down)), sort_by_rx: (KeyCode::Char('z'), Some(KeyCode::Char('Z'))),
toggle_help: (KeyCode::Home, Some(KeyCode::End)), sort_by_state: (KeyCode::Char('0'), Some(KeyCode::Char('9'))),
toggle_mouse_capture: (KeyCode::PageDown, Some(KeyCode::PageUp)), sort_by_status: (KeyCode::Char('1'), Some(KeyCode::Char('8'))),
sort_by_tx: (KeyCode::Char('2'), Some(KeyCode::Char('7'))),
sort_reset: (KeyCode::Char('3'), Some(KeyCode::Char('6'))),
toggle_help: (KeyCode::Char('4'), Some(KeyCode::Char('5'))),
toggle_mouse_capture: (KeyCode::Char('5'), Some(KeyCode::PageDown)),
}; };
setup setup
@@ -662,39 +706,43 @@ mod tests {
#[test] #[test]
/// Help panel will show custom keymap if in use, with either one or two definition for each entry /// Help panel will show custom keymap if in use, with either one or two definition for each entry
fn test_draw_blocks_help_one_and_two_definitions() { fn test_draw_blocks_help_one_and_two_definitions() {
let mut setup = test_setup(110, 47, true, true); let mut setup = test_setup(110, 50, true, true);
let keymap = Keymap { let keymap = Keymap {
clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))), clear: (KeyCode::Char('a'), Some(KeyCode::Char('A'))),
delete_deny: (KeyCode::Char('c'), None), delete_confirm: (KeyCode::Char('b'), None),
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))), delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('C'))),
exec: (KeyCode::Char('g'), None), exec: (KeyCode::Char('d'), None),
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), filter_mode: (KeyCode::Char('e'), Some(KeyCode::Char('E'))),
log_section_height_decrease: (KeyCode::Char('A'), Some(KeyCode::Char('Z'))), force_redraw: (KeyCode::Char('f'), None),
log_section_height_increase: (KeyCode::Char('B'), Some(KeyCode::Char('X'))), log_scroll_back: (KeyCode::Char('g'), Some(KeyCode::Char('G'))),
log_section_toggle: (KeyCode::Char('C'), Some(KeyCode::Char('W'))), log_scroll_forward: (KeyCode::Char('h'), None),
quit: (KeyCode::Char('k'), None), log_section_height_decrease: (KeyCode::Char('i'), Some(KeyCode::Char('I'))),
save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), log_section_height_increase: (KeyCode::Char('j'), None),
scroll_down_many: (KeyCode::Char('o'), None), log_section_toggle: (KeyCode::Char('k'), Some(KeyCode::Char('K'))),
scroll_down_one: (KeyCode::Char('q'), Some(KeyCode::Char('r'))), quit: (KeyCode::Char('l'), None),
scroll_end: (KeyCode::Char('s'), None), save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('M'))),
scroll_start: (KeyCode::Char('u'), Some(KeyCode::Char('v'))), scroll_down_many: (KeyCode::Char('n'), None),
scroll_up_many: (KeyCode::Char('w'), None), scroll_down_one: (KeyCode::Char('o'), Some(KeyCode::Char('O'))),
scroll_up_one: (KeyCode::Char('y'), Some(KeyCode::Char('z'))), scroll_end: (KeyCode::Char('p'), None),
select_next_panel: (KeyCode::Char('0'), None), scroll_many: KeyModifiers::ALT,
select_previous_panel: (KeyCode::Char('2'), Some(KeyCode::Char('3'))), scroll_start: (KeyCode::Char('q'), Some(KeyCode::Char('Q'))),
sort_by_name: (KeyCode::Char('4'), None), scroll_up_many: (KeyCode::Char('r'), None),
sort_by_state: (KeyCode::Char('6'), Some(KeyCode::Char('7'))), scroll_up_one: (KeyCode::Char('s'), Some(KeyCode::Char('S'))),
sort_by_status: (KeyCode::Char('8'), None), select_next_panel: (KeyCode::Char('t'), None),
sort_by_cpu: (KeyCode::F(1), Some(KeyCode::F(12))), select_previous_panel: (KeyCode::Char('u'), Some(KeyCode::Char('U'))),
sort_by_memory: (KeyCode::Char('#'), None), sort_by_cpu: (KeyCode::Char('v'), None),
sort_by_id: (KeyCode::Char('/'), Some(KeyCode::Char('='))), sort_by_id: (KeyCode::Char('w'), Some(KeyCode::Char('W'))),
sort_by_image: (KeyCode::Char(','), None), sort_by_image: (KeyCode::Char('x'), None),
sort_by_rx: (KeyCode::Char('.'), Some(KeyCode::Char(']'))), sort_by_memory: (KeyCode::Char('y'), Some(KeyCode::Char('Y'))),
sort_by_tx: (KeyCode::Insert, None), sort_by_name: (KeyCode::Char('z'), None),
sort_reset: (KeyCode::Up, Some(KeyCode::Down)), sort_by_rx: (KeyCode::Char('0'), Some(KeyCode::Char('9'))),
toggle_help: (KeyCode::Home, None), sort_by_state: (KeyCode::Char('1'), None),
toggle_mouse_capture: (KeyCode::PageDown, Some(KeyCode::PageUp)), sort_by_status: (KeyCode::Char('2'), Some(KeyCode::Char('7'))),
sort_by_tx: (KeyCode::Char('3'), None),
sort_reset: (KeyCode::Char('4'), Some(KeyCode::Char('5'))),
toggle_help: (KeyCode::Char('5'), None),
toggle_mouse_capture: (KeyCode::Char('6'), Some(KeyCode::Char('7'))),
}; };
let tz = setup.app_data.lock().config.timezone.clone(); let tz = setup.app_data.lock().config.timezone.clone();
@@ -731,10 +779,10 @@ mod tests {
for (row_index, result_row) in get_result(&setup) { for (row_index, result_row) in get_result(&setup) {
for (result_cell_index, result_cell) in result_row.iter().enumerate() { for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) { match (row_index, result_cell_index) {
(14, 31..=45) => { (13, 31..=45) => {
assert_eq!(result_cell.fg, AppColors::new().popup_help.text); assert_eq!(result_cell.fg, AppColors::new().popup_help.text);
} }
(14, 46..=55) => { (13, 46..=55) => {
assert_eq!(result_cell.fg, AppColors::new().popup_help.text_highlight); assert_eq!(result_cell.fg, AppColors::new().popup_help.text_highlight);
} }
_ => (), _ => (),
+1 -2
View File
@@ -40,7 +40,7 @@ pub fn draw(
f.render_widget(paragraph, area); f.render_widget(paragraph, area);
} else { } else {
let padding = usize::from(area.height / 5); let padding = usize::from(area.height / 5);
let logs = app_data.lock().get_logs(area.height, padding); let logs = app_data.lock().get_logs(area.as_size(), padding);
if logs.is_empty() { if logs.is_empty() {
let mut paragraph = Paragraph::new("no logs found") let mut paragraph = Paragraph::new("no logs found")
.block(block) .block(block)
@@ -356,7 +356,6 @@ mod tests {
insert_logs(&setup); insert_logs(&setup);
let fd = FrameData::from((&setup.app_data, &setup.gui_state)); let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup setup
.terminal .terminal
.draw(|f| { .draw(|f| {
+23 -14
View File
@@ -72,7 +72,6 @@ pub fn max_line_width(text: &str) -> usize {
.max() .max()
.unwrap_or_default() .unwrap_or_default()
} }
/// Generate block, add a border if is the selected panel, /// Generate block, add a border if is the selected panel,
/// add custom title based on state of each panel /// add custom title based on state of each panel
fn generate_block<'a>( fn generate_block<'a>(
@@ -101,7 +100,15 @@ fn generate_block<'a>(
let mut block = Block::default() let mut block = Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.border_type(BorderType::Rounded) .border_type(BorderType::Rounded)
.title(title); .title(ratatui::text::Line::from(title).left_aligned());
if panel == SelectablePanel::Logs {
if let Some(x) = fd.scroll_title.as_ref() {
block = block
.title_bottom(x.to_owned())
.title_alignment(ratatui::layout::Alignment::Right);
}
}
if !fd.status.contains(&Status::Filter) { if !fd.status.contains(&Status::Filter) {
if fd.selected_panel == panel { if fd.selected_panel == panel {
block = block.border_style(Style::default().fg(colors.borders.selected)); block = block.border_style(Style::default().fg(colors.borders.selected));
@@ -151,7 +158,7 @@ pub mod tests {
/// Create a FrameData struct from two Arc<mutex>'s, instead of from UI /// Create a FrameData struct from two Arc<mutex>'s, instead of from UI
impl From<(&Arc<Mutex<AppData>>, &Arc<Mutex<GuiState>>)> for FrameData { impl From<(&Arc<Mutex<AppData>>, &Arc<Mutex<GuiState>>)> for FrameData {
fn from(data: (&Arc<Mutex<AppData>>, &Arc<Mutex<GuiState>>)) -> Self { fn from(data: (&Arc<Mutex<AppData>>, &Arc<Mutex<GuiState>>)) -> Self {
let (app_data, gui_data) = (data.0.lock(), data.1.lock()); let (mut app_data, gui_data) = (data.0.lock(), data.1.lock());
// let container_section_height = app_data.get_container_len(); // let container_section_height = app_data.get_container_len();
// let container_section_height = if container_section_height < 12 { // let container_section_height = if container_section_height < 12 {
@@ -178,6 +185,7 @@ pub mod tests {
loading_icon: gui_data.get_loading().to_string(), loading_icon: gui_data.get_loading().to_string(),
log_height: gui_data.get_log_height(), log_height: gui_data.get_log_height(),
log_title: app_data.get_log_title(), log_title: app_data.get_log_title(),
scroll_title: app_data.get_scroll_title(gui_data.get_screen_width()),
port_max_lens: app_data.get_longest_port(), port_max_lens: app_data.get_longest_port(),
ports: app_data.get_selected_ports(), ports: app_data.get_selected_ports(),
selected_panel: gui_data.get_selected_panel(), selected_panel: gui_data.get_selected_panel(),
@@ -208,6 +216,7 @@ pub mod tests {
let gui_state = Arc::new(Mutex::new(gui_state)); let gui_state = Arc::new(Mutex::new(gui_state));
let fd = FrameData::from((&app_data, &gui_state)); let fd = FrameData::from((&app_data, &gui_state));
let area = Rect::new(0, 0, w, h); let area = Rect::new(0, 0, w, h);
gui_state.lock().set_screen_width(w);
TuiTestSetup { TuiTestSetup {
app_data, app_data,
gui_state, gui_state,
@@ -220,9 +229,9 @@ pub mod tests {
/// Just a shorthand for when enumerating over result cells /// Just a shorthand for when enumerating over result cells
pub fn get_result( pub fn get_result(
setup: &TuiTestSetup, setup: &'_ TuiTestSetup,
// w: u16, // w: u16,
) -> std::iter::Enumerate<std::slice::Chunks<ratatui::buffer::Cell>> { ) -> std::iter::Enumerate<std::slice::Chunks<'_, ratatui::buffer::Cell>> {
setup setup
.terminal .terminal
.backend() .backend()
@@ -276,7 +285,7 @@ pub mod tests {
setup.app_data.lock().containers.items[0] setup.app_data.lock().containers.items[0]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
@@ -305,7 +314,7 @@ pub mod tests {
setup.app_data.lock().containers.items[1] setup.app_data.lock().containers.items[1]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
@@ -340,7 +349,7 @@ pub mod tests {
setup.app_data.lock().containers.items[0] setup.app_data.lock().containers.items[0]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
@@ -373,7 +382,7 @@ pub mod tests {
setup.app_data.lock().containers.items[0] setup.app_data.lock().containers.items[0]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
@@ -402,7 +411,7 @@ pub mod tests {
setup.app_data.lock().containers.items[0] setup.app_data.lock().containers.items[0]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
@@ -434,7 +443,7 @@ pub mod tests {
setup.app_data.lock().containers.items[0] setup.app_data.lock().containers.items[0]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
@@ -464,7 +473,7 @@ pub mod tests {
setup.app_data.lock().containers.items[0] setup.app_data.lock().containers.items[0]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
@@ -498,7 +507,7 @@ pub mod tests {
setup.app_data.lock().containers.items[0] setup.app_data.lock().containers.items[0]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
@@ -530,7 +539,7 @@ pub mod tests {
setup.app_data.lock().containers.items[0] setup.app_data.lock().containers.items[0]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
+1 -1
View File
@@ -182,7 +182,7 @@ mod tests {
setup.app_data.lock().containers.items[0] setup.app_data.lock().containers.items[0]
.ports .ports
.push(ContainerPorts { .push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
private: 8003, private: 8003,
public: Some(8003), public: Some(8003),
}); });
@@ -1,6 +1,5 @@
--- ---
source: src/ui/draw_blocks/help.rs source: src/ui/draw_blocks/help.rs
assertion_line: 456
expression: setup.terminal.backend() expression: setup.terminal.backend()
--- ---
" " " "
@@ -18,9 +17,12 @@ expression: setup.terminal.backend()
" │ A simple tui to view & control docker containers │ " " │ A simple tui to view & control docker containers │ "
" │ │ " " │ │ "
" │ ( tab ) or ( shift+tab ) change panels │ " " │ ( tab ) or ( shift+tab ) change panels │ "
" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ " " │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) scroll vertically │ "
" │ ( ← → ) horizontal scroll across logs │ "
" │ ( ctrl ) increase scroll speed, used in conjuction scroll keys │ "
" │ ( enter ) send docker container command │ " " │ ( enter ) send docker container command │ "
" │ ( e ) exec into a container │ " " │ ( e ) exec into a container │ "
" │ ( f ) force clear the screen & redraw the gui │ "
" │ ( h ) toggle this help information - or click heading │ " " │ ( h ) toggle this help information - or click heading │ "
" │ ( s ) save logs to file │ " " │ ( s ) save logs to file │ "
" │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ " " │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
@@ -35,6 +37,5 @@ expression: setup.terminal.backend()
" │ currently an early work in progress, all and any input appreciated │ " " │ currently an early work in progress, all and any input appreciated │ "
" │ https://github.com/mrjackwills/oxker │ " " │ https://github.com/mrjackwills/oxker │ "
" │ │ " " │ │ "
" │ │ "
" ╰───────────────────────────────────────────────────────────────────────────────────╯ " " ╰───────────────────────────────────────────────────────────────────────────────────╯ "
" " " "
@@ -17,9 +17,12 @@ expression: setup.terminal.backend()
" │ A simple tui to view & control docker containers │ " " │ A simple tui to view & control docker containers │ "
" │ │ " " │ │ "
" │ ( tab ) or ( shift+tab ) change panels │ " " │ ( tab ) or ( shift+tab ) change panels │ "
" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ " " │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) scroll vertically │ "
" │ ( ← → ) horizontal scroll across logs │ "
" │ ( ctrl ) increase scroll speed, used in conjuction scroll keys │ "
" │ ( enter ) send docker container command │ " " │ ( enter ) send docker container command │ "
" │ ( e ) exec into a container │ " " │ ( e ) exec into a container │ "
" │ ( f ) force clear the screen & redraw the gui │ "
" │ ( h ) toggle this help information - or click heading │ " " │ ( h ) toggle this help information - or click heading │ "
" │ ( s ) save logs to file │ " " │ ( s ) save logs to file │ "
" │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ " " │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
@@ -34,6 +37,5 @@ expression: setup.terminal.backend()
" │ currently an early work in progress, all and any input appreciated │ " " │ currently an early work in progress, all and any input appreciated │ "
" │ https://github.com/mrjackwills/oxker │ " " │ https://github.com/mrjackwills/oxker │ "
" │ │ " " │ │ "
" │ │ "
" ╰───────────────────────────────────────────────────────────────────────────────────╯ " " ╰───────────────────────────────────────────────────────────────────────────────────╯ "
" " " "
@@ -2,7 +2,7 @@
source: src/ui/draw_blocks/help.rs source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend() expression: setup.terminal.backend()
--- ---
" ╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────────────╮ " " ╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────╮ "
" │ │ " " │ │ "
" │ 88 │ " " │ 88 │ "
" │ 88 │ " " │ 88 │ "
@@ -15,37 +15,40 @@ expression: setup.terminal.backend()
" │ │ " " │ │ "
" │ A simple tui to view & control docker containers │ " " │ A simple tui to view & control docker containers │ "
" │ │ " " │ │ "
" │ ( 0 ) select next panel " " │ ( t ) select next panel "
" │ ( 2 ) select previous panel " " │ ( u ) select previous panel "
" │ ( q ) scroll list down by one " " │ ( o ) scroll list down by one "
" │ ( y ) scroll list up by one " " │ ( s ) scroll list up by one "
" │ ( o ) scroll list down by many " " │ ( n ) scroll list down by many "
" │ ( w ) scroll list by up many " " │ ( r ) scroll list by up many "
" │ ( s ) scroll list to end " " │ ( p ) scroll list to end "
" │ ( u ) scroll list to start " " │ ( q ) scroll list to start "
" │ ( h ) horizontal scroll logs right │ "
" │ ( g ) horizontal scroll logs left │ "
" │ ( Alt ) increase scroll speed, used in conjuction scroll keys │ "
" │ ( enter ) send docker container command │ " " │ ( enter ) send docker container command │ "
" │ ( g ) exec into a container " " │ ( d ) exec into a container "
" │ ( Home ) toggle this help information - or click heading │ " " │ ( f ) force clear the screen & redraw the gui "
" │ ( 5 ) toggle this help information - or click heading │ "
" │ ( m ) save logs to file │ " " │ ( m ) save logs to file │ "
" │ ( Page Down ) toggle mouse capture - if disabled, text on screen can be selected & copied │ " " │ ( 6 ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
" │ ( i ) enter filter mode " " │ ( e ) enter filter mode "
" │ ( Up ) reset container sorting " " │ ( 4 ) reset container sorting "
" │ ( 4 ) sort containers by name " " │ ( z ) sort containers by name "
" │ ( 6 ) sort containers by state " " │ ( 1 ) sort containers by state "
" │ ( 8 ) sort containers by status " " │ ( 2 ) sort containers by status "
" │ ( F1 ) sort containers by cpu " " │ ( v ) sort containers by cpu "
" │ ( # ) sort containers by memory " " │ ( y ) sort containers by memory "
" │ ( / ) sort containers by id " " │ ( w ) sort containers by id "
" │ ( , ) sort containers by image " " │ ( x ) sort containers by image "
" │ ( . ) sort containers by rx " " │ ( 0 ) sort containers by rx "
" │ ( Insert ) sort containers by tx " " │ ( 3 ) sort containers by tx "
" │ ( z ) decrease log section height " " │ ( i ) decrease log section height "
" │ ( x ) increase log section height " " │ ( j ) increase log section height "
" │ ( W ) toggle log section visibility " " │ ( k ) toggle log section visibility "
" │ ( a ) close dialog │ " " │ ( a ) close dialog │ "
" │ ( k ) quit at any time " " │ ( l ) quit at any time "
" │ │ " " │ │ "
" │ currently an early work in progress, all and any input appreciated │ " " │ currently an early work in progress, all and any input appreciated │ "
" │ https://github.com/mrjackwills/oxker │ " " │ https://github.com/mrjackwills/oxker │ "
" " " ╰────────────────────────────────────────────────────────────────────────────────────╯ "
" ╰────────────────────────────────────────────────────────────────────────────────────────────╯ "
@@ -2,7 +2,7 @@
source: src/ui/draw_blocks/help.rs source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend() expression: setup.terminal.backend()
--- ---
" ╭ 0.00.000 ────────────────────────────────────────────────────────────────────────────────────────────────╮ " " ╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────────────────────╮ "
" │ │ " " │ │ "
" │ 88 │ " " │ 88 │ "
" │ 88 │ " " │ 88 │ "
@@ -15,37 +15,40 @@ expression: setup.terminal.backend()
" │ │ " " │ │ "
" │ A simple tui to view & control docker containers │ " " │ A simple tui to view & control docker containers │ "
" │ │ " " │ │ "
" │ ( 0 ) or ( 1 ) select next panel " " │ ( s ) or ( S ) select next panel "
" │ ( 2 ) or ( 3 ) select previous panel " " │ ( t ) or ( T ) select previous panel "
" │ ( q ) or ( r ) scroll list down by one " " │ ( n ) or ( N ) scroll list down by one "
" │ ( y ) or ( z ) scroll list up by one " " │ ( r ) or ( R ) scroll list up by one "
" │ ( o ) or ( p ) scroll list down by many " " │ ( m ) or ( M ) scroll list down by many "
" │ ( w ) or ( x ) scroll list by up many " " │ ( q ) or ( Q ) scroll list by up many "
" │ ( s ) or ( t ) scroll list to end " " │ ( o ) or ( O ) scroll list to end "
" │ ( u ) or ( v ) scroll list to start " " │ ( p ) or ( P ) scroll list to start "
" │ ( g ) or ( G ) horizontal scroll logs right │ "
" │ ( f ) or ( F ) horizontal scroll logs left │ "
" │ ( Alt ) increase scroll speed, used in conjuction scroll keys │ "
" │ ( enter ) send docker container command │ " " │ ( enter ) send docker container command │ "
" │ ( g ) or ( h ) exec into a container " " │ ( d ) or ( D ) exec into a container "
" │ ( Home ) or ( End ) toggle this help information - or click heading │ " " │ ( f ) or ( F ) force clear the screen & redraw the gui "
" │ ( m ) or ( n ) save logs to file │ " " │ ( 4 ) or ( 5 ) toggle this help information - or click heading "
" │ ( Page Down ) or ( Page Up ) toggle mouse capture - if disabled, text on screen can be selected & copied │ " " │ ( l ) or ( L ) save logs to file │ "
" │ ( i ) or ( j ) enter filter mode " " │ ( 5 ) or ( Page Down ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
" │ ( Up ) or ( Down ) reset container sorting │ " " │ ( e ) or ( E ) enter filter mode "
" │ ( 4 ) or ( 5 ) sort containers by name │ " " │ ( 3 ) or ( 6 ) reset container sorting "
" │ ( 6 ) or ( 7 ) sort containers by state " " │ ( y ) or ( Y ) sort containers by name "
" │ ( 8 ) or ( 9 ) sort containers by status │ " " │ ( 0 ) or ( 9 ) sort containers by state "
" │ ( F1 ) or ( F12 ) sort containers by cpu │ " " │ ( 1 ) or ( 8 ) sort containers by status "
" │ ( # ) or ( - ) sort containers by memory │ " " │ ( u ) or ( U ) sort containers by cpu "
" │ ( / ) or ( = ) sort containers by id │ " " │ ( x ) or ( X ) sort containers by memory "
" │ ( , ) or ( \ ) sort containers by image │ " " │ ( v ) or ( V ) sort containers by id "
" │ ( . ) or ( ] ) sort containers by rx │ " " │ ( w ) or ( W ) sort containers by image "
" │ ( Insert ) or ( Back Tab ) sort containers by tx │ " " │ ( z ) or ( Z ) sort containers by rx "
" │ ( A ) or ( Z ) decrease log section height │ " " │ ( 2 ) or ( 7 ) sort containers by tx "
" │ ( B ) or ( X ) increase log section height " " │ ( h ) or ( H ) decrease log section height "
" │ ( C ) or ( W ) toggle log section visibility │ " " │ ( i ) or ( I ) increase log section height "
" │ ( a ) or ( b ) close dialog │ " " │ ( j ) or ( J ) toggle log section visibility "
" │ ( k ) or ( l ) quit at any time │ " " │ ( a ) or ( A ) close dialog "
" │ ( k ) or ( K ) quit at any time │ "
" │ │ " " │ │ "
" │ currently an early work in progress, all and any input appreciated │ " " │ currently an early work in progress, all and any input appreciated │ "
" │ https://github.com/mrjackwills/oxker │ " " │ https://github.com/mrjackwills/oxker │ "
" " " ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ "
" ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ "
@@ -2,7 +2,7 @@
source: src/ui/draw_blocks/help.rs source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend() expression: setup.terminal.backend()
--- ---
" ╭ 0.00.000 ────────────────────────────────────────────────────────────────────────────────────────────────╮ " " ╭ 0.00.000 ────────────────────────────────────────────────────────────────────────────────── "
" │ │ " " │ │ "
" │ 88 │ " " │ 88 │ "
" │ 88 │ " " │ 88 │ "
@@ -15,37 +15,40 @@ expression: setup.terminal.backend()
" │ │ " " │ │ "
" │ A simple tui to view & control docker containers │ " " │ A simple tui to view & control docker containers │ "
" │ │ " " │ │ "
" │ ( 0 ) select next panel " " │ ( t ) select next panel "
" │ ( 2 ) or ( 3 ) select previous panel " " │ ( u ) or ( U ) select previous panel "
" │ ( q ) or ( r ) scroll list down by one " " │ ( o ) or ( O ) scroll list down by one "
" │ ( y ) or ( z ) scroll list up by one " " │ ( s ) or ( S ) scroll list up by one "
" │ ( o ) scroll list down by many " " │ ( n ) scroll list down by many "
" │ ( w ) scroll list by up many " " │ ( r ) scroll list by up many "
" │ ( s ) scroll list to end " " │ ( p ) scroll list to end "
" │ ( u ) or ( v ) scroll list to start " " │ ( q ) or ( Q ) scroll list to start "
" │ ( h ) horizontal scroll logs right │ "
" │ ( g ) or ( G ) horizontal scroll logs left │ "
" │ ( Alt ) increase scroll speed, used in conjuction scroll keys │ "
" │ ( enter ) send docker container command │ " " │ ( enter ) send docker container command │ "
" │ ( g ) exec into a container " " │ ( d ) exec into a container "
" │ ( Home ) toggle this help information - or click heading │ " " │ ( f ) force clear the screen & redraw the gui "
" │ ( m ) or ( n ) save logs to file │ " " │ ( 5 ) toggle this help information - or click heading "
" │ ( Page Down ) or ( Page Up ) toggle mouse capture - if disabled, text on screen can be selected & copied │ " " │ ( m ) or ( M ) save logs to file │ "
" │ ( i ) or ( j ) enter filter mode " " │ ( 6 ) or ( 7 ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
" │ ( Up ) or ( Down ) reset container sorting │ " " │ ( e ) or ( E ) enter filter mode "
" │ ( 4 ) sort containers by name │ " " │ ( 4 ) or ( 5 ) reset container sorting "
" │ ( 6 ) or ( 7 ) sort containers by state " " ( z ) sort containers by name "
" │ ( 8 ) sort containers by status │ " " │ ( 1 ) sort containers by state "
" │ ( F1 ) or ( F12 ) sort containers by cpu │ " " │ ( 2 ) or ( 7 ) sort containers by status "
" │ ( # ) sort containers by memory │ " " │ ( v ) sort containers by cpu "
" │ ( / ) or ( = ) sort containers by id │ " " │ ( y ) or ( Y ) sort containers by memory "
" │ ( , ) sort containers by image │ " " │ ( w ) or ( W ) sort containers by id "
" │ ( . ) or ( ] ) sort containers by rx │ " " ( x ) sort containers by image "
" │ ( Insert ) sort containers by tx " " │ ( 0 ) or ( 9 ) sort containers by rx "
" │ ( A ) or ( Z ) decrease log section height │ " " ( 3 ) sort containers by tx "
" │ ( B ) or ( X ) increase log section height " " │ ( i ) or ( I ) decrease log section height "
" │ ( C ) or ( W ) toggle log section visibility │ " " ( j ) increase log section height "
" │ ( a ) or ( b ) close dialog │ " " │ ( k ) or ( K ) toggle log section visibility "
" │ ( k ) quit at any time │ " " │ ( a ) or ( A ) close dialog "
" │ ( l ) quit at any time │ "
" │ │ " " │ │ "
" │ currently an early work in progress, all and any input appreciated │ " " │ currently an early work in progress, all and any input appreciated │ "
" │ https://github.com/mrjackwills/oxker │ " " │ https://github.com/mrjackwills/oxker │ "
" " " ╰────────────────────────────────────────────────────────────────────────────────────────────╯ "
" ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ "
@@ -2,7 +2,6 @@
source: src/ui/draw_blocks/help.rs source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend() expression: setup.terminal.backend()
--- ---
" "
" ╭ 0.00.000 ─────────────────────────────────────────────────────────────────────────╮ " " ╭ 0.00.000 ─────────────────────────────────────────────────────────────────────────╮ "
" │ │ " " │ │ "
" │ 88 │ " " │ 88 │ "
@@ -19,9 +18,12 @@ expression: setup.terminal.backend()
" │ logs timezone: Asia/Tokyo │ " " │ logs timezone: Asia/Tokyo │ "
" │ │ " " │ │ "
" │ ( tab ) or ( shift+tab ) change panels │ " " │ ( tab ) or ( shift+tab ) change panels │ "
" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ " " │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) scroll vertically │ "
" │ ( ← → ) horizontal scroll across logs │ "
" │ ( ctrl ) increase scroll speed, used in conjuction scroll keys │ "
" │ ( enter ) send docker container command │ " " │ ( enter ) send docker container command │ "
" │ ( e ) exec into a container │ " " │ ( e ) exec into a container │ "
" │ ( f ) force clear the screen & redraw the gui │ "
" │ ( h ) toggle this help information - or click heading │ " " │ ( h ) toggle this help information - or click heading │ "
" │ ( s ) save logs to file │ " " │ ( s ) save logs to file │ "
" │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ " " │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ "
@@ -36,6 +38,4 @@ expression: setup.terminal.backend()
" │ currently an early work in progress, all and any input appreciated │ " " │ currently an early work in progress, all and any input appreciated │ "
" │ https://github.com/mrjackwills/oxker │ " " │ https://github.com/mrjackwills/oxker │ "
" │ │ " " │ │ "
" │ │ "
" ╰───────────────────────────────────────────────────────────────────────────────────╯ " " ╰───────────────────────────────────────────────────────────────────────────────────╯ "
" "
@@ -4,25 +4,26 @@ expression: setup.terminal.backend()
--- ---
" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) exit help " " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) exit help "
"╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮" "╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
"│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │" Hidden by multi-width symbols: [(2, " ")] "│⚪ container_1 ✓ running Up 1 ho 0.00.000 ──────────────────────────────────────────────────────────────────────────╮ ││▶ pause │" Hidden by multi-width symbols: [(2, " ")]
"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │" "│ container_2 ✓ running Up 2 ho ││ restart │"
"│ container_3 ✓ running Up 3 ho╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────╮ ││ stop │" "│ container_3 ✓ running Up 3 ho│ 88 │ ││ stop │"
"│ │ │ ││ delete │" "│ │ 88 │ ││ delete │"
"│ │ 88 │ ││ │" "│ │ 88 │ ││ │"
"╰────────────────────────────────────│ 88 │────────────────────╯╰──────────────╯" "╰────────────────────────────────────│ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │────────────────────╯╰──────────────╯"
"╭ Logs 3/3 - container_1 - image_1 ──│ 88 │────────────────────────────────────╮" "╭ Logs 3/3 - container_1 - image_1 ──│ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │────────────────────────────────────╮"
"│ line 1 │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ │" "│ line 1 │ 8b d8 )888( 8888[ 8PP""""""" 88 │ │"
"│ line 2 │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ │" "│ line 2 │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ │"
"│▶ line 3 │ 8b d8 )888( 8888[ 8PP""""""" 88 │ │" "│▶ line 3 │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ │"
"│ │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ │"
"│ │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ │"
"│ │ │ │" "│ │ │ │"
"│ │ A simple tui to view & control docker containers │ │" "│ │ A simple tui to view & control docker containers │ │"
"│ │ │ │" "│ │ │ │"
"│ │ ( tab ) or ( shift+tab ) change panels │ │" "│ │ ( tab ) or ( shift+tab ) change panels │ │"
"│ │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ │" "│ │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) scroll vertically │ │"
"│ │ ( ← → ) horizontal scroll across logs │ │"
"│ │ ( ctrl ) increase scroll speed, used in conjuction scroll keys │ │"
"│ │ ( enter ) send docker container command │ │" "│ │ ( enter ) send docker container command │ │"
"│ │ ( e ) exec into a container │ │" "│ │ ( e ) exec into a container │ │"
"│ │ ( f ) force clear the screen & redraw the gui │ │"
"│ │ ( h ) toggle this help information - or click heading │ │" "│ │ ( h ) toggle this help information - or click heading │ │"
"│ │ ( s ) save logs to file │ │" "│ │ ( s ) save logs to file │ │"
"│ │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ │" "│ │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ │"
@@ -30,15 +31,14 @@ expression: setup.terminal.backend()
"│ │ ( 0 ) stop sort │ │" "│ │ ( 0 ) stop sort │ │"
"│ │ ( 1 - 9 ) sort by header - or click header │ │" "│ │ ( 1 - 9 ) sort by header - or click header │ │"
"│ │ ( - = ) change log section height │ │" "│ │ ( - = ) change log section height │ │"
"│ ( \ ) toggle log section visibility │" "╰────────────────────────────────────│ ( \ ) toggle log section visibility │────────────────────────────────────╯"
"────────────────────────────────────│ ( esc ) close dialog │────────────────────────────────────" "───────────────────────── cpu 03.00%│ ( esc ) close dialog │──────╮╭────────── ports ───────────"
"╭───────────────────────── cpu 03.00%│ ( q ) quit at any time │──────╮╭────────── ports ───────────╮" "│10.00%│ •• │ ( q ) quit at any time │ ││ ip private public│"
"│10.00%│ •• │ │ ││ ip private public│" "│ • │ │ ││ 8001 │"
"│ │ • • │ currently an early work in progress, all and any input appreciated │ ││ 8001 │" "│ │ • • │ currently an early work in progress, all and any input appreciated │ ││127.0.0.1 8003 8003│"
"│ │ • • │ https://github.com/mrjackwills/oxker │ ││127.0.0.1 8003 8003│" "│ │ • • │ https://github.com/mrjackwills/oxker │ ││ │"
"│ │ │ │ ││ │" "│ │ •• │ │ ││ │"
"│ │• • ╰────────────────────────────────────────────────────────────────────────────────────╯ ││ │" "│ │• •• │ │ ││ │"
"│ │• •• ││ │• •• ││ │" "│ │• • ╰────────────────────────────────────────────────────────────────────────────────────╯ ││ │"
"│ │• • ││ │• • ││ │"
"│ │ ││ │ ││ │" "│ │ ││ │ ││ │"
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯" "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
+31 -14
View File
@@ -187,6 +187,7 @@ pub struct GuiState {
log_height: u16, log_height: u16,
rerender: Arc<Rerender>, rerender: Arc<Rerender>,
selected_panel: SelectablePanel, selected_panel: SelectablePanel,
screen_width: u16,
show_logs: bool, show_logs: bool,
status: HashSet<Status>, status: HashSet<Status>,
pub info_box_text: Option<(String, Instant)>, pub info_box_text: Option<(String, Instant)>,
@@ -205,6 +206,7 @@ impl GuiState {
loading_index: 0, loading_index: 0,
loading_set: HashSet::new(), loading_set: HashSet::new(),
log_height: 75, log_height: 75,
screen_width: 0,
rerender: Arc::clone(redraw), rerender: Arc::clone(redraw),
selected_panel: SelectablePanel::default(), selected_panel: SelectablePanel::default(),
show_logs, show_logs,
@@ -215,7 +217,7 @@ impl GuiState {
pub fn log_height_increase(&mut self) { pub fn log_height_increase(&mut self) {
if self.show_logs && self.log_height <= 75 { if self.show_logs && self.log_height <= 75 {
self.log_height = self.log_height.saturating_add(5); self.log_height = self.log_height.saturating_add(5);
self.rerender.update(); self.rerender.update_draw();
} }
} }
@@ -228,10 +230,20 @@ impl GuiState {
self.show_logs = false; self.show_logs = false;
self.selected_panel = SelectablePanel::Containers; self.selected_panel = SelectablePanel::Containers;
} }
self.rerender.update(); self.rerender.update_draw();
} }
} }
/// Set the screen width, used for offset char calculations
pub const fn set_screen_width(&mut self, width: u16) {
self.screen_width = width;
}
/// Get the screen width, used for offset char calculations
pub const fn get_screen_width(&self) -> u16 {
self.screen_width
}
pub const fn get_show_logs(&self) -> bool { pub const fn get_show_logs(&self) -> bool {
self.show_logs self.show_logs
} }
@@ -241,7 +253,7 @@ impl GuiState {
if !self.show_logs && self.selected_panel == SelectablePanel::Logs { if !self.show_logs && self.selected_panel == SelectablePanel::Logs {
self.selected_panel = SelectablePanel::Containers; self.selected_panel = SelectablePanel::Containers;
} }
self.rerender.update(); self.rerender.update_draw();
} }
/// Set the log_height to zero, for now only used by tests /// Set the log_height to zero, for now only used by tests
@@ -260,6 +272,11 @@ impl GuiState {
self.intersect_panel.clear(); self.intersect_panel.clear();
} }
/// Set the rerender clear to true, to flush the screen and redraw
pub fn set_clear(&self) {
self.rerender.set_clear();
}
/// Get the currently selected panel /// Get the currently selected panel
pub const fn get_selected_panel(&self) -> SelectablePanel { pub const fn get_selected_panel(&self) -> SelectablePanel {
self.selected_panel self.selected_panel
@@ -275,7 +292,7 @@ impl GuiState {
.first() .first()
{ {
self.selected_panel = *data.0; self.selected_panel = *data.0;
self.rerender.update(); self.rerender.update_draw();
} }
} }
@@ -348,7 +365,7 @@ impl GuiState {
self.status_del(Status::DeleteConfirm); self.status_del(Status::DeleteConfirm);
} }
self.delete_container_id = id; self.delete_container_id = id;
self.rerender.update(); self.rerender.update_draw();
} }
/// Return a copy of the Status HashSet /// Return a copy of the Status HashSet
@@ -369,7 +386,7 @@ impl GuiState {
} }
_ => (), _ => (),
} }
self.rerender.update(); self.rerender.update_draw();
} }
/// Inset the ExecMode into self, and set the Status as exec /// Inset the ExecMode into self, and set the Status as exec
@@ -378,7 +395,7 @@ impl GuiState {
pub fn set_exec_mode(&mut self, mode: ExecMode) { pub fn set_exec_mode(&mut self, mode: ExecMode) {
self.exec_mode = Some(mode); self.exec_mode = Some(mode);
self.status.insert(Status::Exec); self.status.insert(Status::Exec);
self.rerender.update(); self.rerender.update_draw();
} }
pub fn get_exec_mode(&self) -> Option<ExecMode> { pub fn get_exec_mode(&self) -> Option<ExecMode> {
@@ -390,7 +407,7 @@ impl GuiState {
pub fn status_push(&mut self, status: Status) { pub fn status_push(&mut self, status: Status) {
if status != Status::Exec { if status != Status::Exec {
self.status.insert(status); self.status.insert(status);
self.rerender.update(); self.rerender.update_draw();
} }
} }
@@ -403,7 +420,7 @@ impl GuiState {
{ {
self.selected_panel = self.selected_panel.next(); self.selected_panel = self.selected_panel.next();
} }
self.rerender.update(); self.rerender.update_draw();
} }
/// Change to previous selectable panel /// Change to previous selectable panel
@@ -415,7 +432,7 @@ impl GuiState {
{ {
self.selected_panel = self.selected_panel.prev(); self.selected_panel = self.selected_panel.prev();
} }
self.rerender.update(); self.rerender.update_draw();
} }
/// Insert a new loading_uuid into HashSet, and advance the loading_index by one frame, or reset to 0 if at end of array /// Insert a new loading_uuid into HashSet, and advance the loading_index by one frame, or reset to 0 if at end of array
@@ -426,7 +443,7 @@ impl GuiState {
self.loading_index += 1; self.loading_index += 1;
} }
self.loading_set.insert(uuid); self.loading_set.insert(uuid);
self.rerender.update(); self.rerender.update_draw();
} }
pub fn is_loading(&self) -> bool { pub fn is_loading(&self) -> bool {
@@ -459,7 +476,7 @@ impl GuiState {
/// Stop the loading_spin function, and reset gui loading status /// Stop the loading_spin function, and reset gui loading status
pub fn stop_loading_animation(&mut self, loading_uuid: Uuid) { pub fn stop_loading_animation(&mut self, loading_uuid: Uuid) {
self.loading_set.remove(&loading_uuid); self.loading_set.remove(&loading_uuid);
self.rerender.update(); self.rerender.update_draw();
if self.loading_set.is_empty() { if self.loading_set.is_empty() {
self.loading_index = 0; self.loading_index = 0;
if let Some(h) = &self.loading_handle { if let Some(h) = &self.loading_handle {
@@ -472,12 +489,12 @@ impl GuiState {
/// Set info box content /// Set info box content
pub fn set_info_box(&mut self, text: &str) { pub fn set_info_box(&mut self, text: &str) {
self.info_box_text = Some((text.to_owned(), std::time::Instant::now())); self.info_box_text = Some((text.to_owned(), std::time::Instant::now()));
self.rerender.update(); self.rerender.update_draw();
} }
/// Remove info box content /// Remove info box content
pub fn reset_info_box(&mut self) { pub fn reset_info_box(&mut self) {
self.info_box_text = None; self.info_box_text = None;
self.rerender.update(); self.rerender.update_draw();
} }
} }
+30 -10
View File
@@ -50,7 +50,7 @@ pub struct Ui {
input_tx: Sender<InputMessages>, input_tx: Sender<InputMessages>,
is_running: Arc<AtomicBool>, is_running: Arc<AtomicBool>,
now: Instant, now: Instant,
redraw: Arc<Rerender>, rerender: Arc<Rerender>,
terminal: Terminal<CrosstermBackend<Stdout>>, terminal: Terminal<CrosstermBackend<Stdout>>,
} }
@@ -73,7 +73,7 @@ impl Ui {
gui_state: Arc<Mutex<GuiState>>, gui_state: Arc<Mutex<GuiState>>,
input_tx: Sender<InputMessages>, input_tx: Sender<InputMessages>,
is_running: Arc<AtomicBool>, is_running: Arc<AtomicBool>,
redraw: Arc<Rerender>, rerender: Arc<Rerender>,
) { ) {
match Self::setup_terminal() { match Self::setup_terminal() {
Ok(mut terminal) => { Ok(mut terminal) => {
@@ -85,7 +85,7 @@ impl Ui {
input_tx, input_tx,
is_running, is_running,
now: Instant::now(), now: Instant::now(),
redraw, rerender,
terminal, terminal,
}; };
if let Err(e) = ui.draw_ui().await { if let Err(e) = ui.draw_ui().await {
@@ -169,6 +169,13 @@ impl Ui {
Ok(()) Ok(())
} }
/// Check if the user has attempt to clear the screen, and if so clear and redraw
fn check_clear(&mut self) {
if self.rerender.get_clear() {
self.terminal.clear().ok();
self.rerender.update_draw();
}
}
/// Use external docker cli to exec into a container /// Use external docker cli to exec into a container
async fn exec(&mut self) { async fn exec(&mut self) {
let exec_mode = self.gui_state.lock().get_exec_mode(); let exec_mode = self.gui_state.lock().get_exec_mode();
@@ -191,7 +198,8 @@ impl Ui {
/// Use the previously redrawn time, the current time, the docker_interval, and the redraw struct, to calculate /// Use the previously redrawn time, the current time, the docker_interval, and the redraw struct, to calculate
/// if the screen should be redrawn or not /// if the screen should be redrawn or not
fn should_redraw(&self, previous: &mut Instant, docker_interval_ms: u128) -> bool { fn should_redraw(&self, previous: &mut Instant, docker_interval_ms: u128) -> bool {
let result = self.redraw.swap() || previous.elapsed().as_millis() >= docker_interval_ms; let result =
self.rerender.swap_draw() || previous.elapsed().as_millis() >= docker_interval_ms;
if result { if result {
*previous = std::time::Instant::now(); *previous = std::time::Instant::now();
} }
@@ -205,7 +213,15 @@ impl Ui {
let docker_interval_ms = u128::from(self.app_data.lock().config.docker_interval_ms); let docker_interval_ms = u128::from(self.app_data.lock().config.docker_interval_ms);
let mut drawn_at = std::time::Instant::now(); let mut drawn_at = std::time::Instant::now();
if let Ok(size) = self.terminal.size() {
self.gui_state.lock().set_screen_width(size.width);
}
while self.is_running.load(Ordering::SeqCst) { while self.is_running.load(Ordering::SeqCst) {
// if self.redraw.get_clear() {
// self.terminal.clear().ok();
// continue;
// }
if self.should_redraw(&mut drawn_at, docker_interval_ms) { if self.should_redraw(&mut drawn_at, docker_interval_ms) {
let fd = FrameData::from(&*self); let fd = FrameData::from(&*self);
@@ -239,18 +255,21 @@ impl Ui {
event::MouseEventKind::Down(_) event::MouseEventKind::Down(_)
| event::MouseEventKind::ScrollDown | event::MouseEventKind::ScrollDown
| event::MouseEventKind::ScrollUp => { | event::MouseEventKind::ScrollUp => {
self.input_tx.send(InputMessages::MouseEvent(m)).await.ok(); self.input_tx
.send(InputMessages::MouseEvent((m, m.modifiers)))
.await
.ok();
} }
_ => (), _ => (),
} }
} else if let Event::Resize(_, _) = event { } else if let Event::Resize(width, _) = event {
self.gui_state.lock().clear_area_map(); self.gui_state.lock().clear_area_map();
// self.gui_state.lock().set_window_height(row);
self.terminal.autoresize().ok(); self.terminal.autoresize().ok();
self.gui_state.lock().set_screen_width(width);
} }
} }
} }
self.check_clear();
} }
Ok(()) Ok(())
} }
@@ -279,7 +298,6 @@ pub struct FrameData {
filter_by: FilterBy, filter_by: FilterBy,
filter_term: Option<String>, filter_term: Option<String>,
has_containers: bool, has_containers: bool,
// container_section_height: u16,
log_height: u16, log_height: u16,
show_logs: bool, show_logs: bool,
has_error: Option<AppError>, has_error: Option<AppError>,
@@ -290,13 +308,14 @@ pub struct FrameData {
port_max_lens: (usize, usize, usize), port_max_lens: (usize, usize, usize),
ports: Option<(Vec<ContainerPorts>, State)>, ports: Option<(Vec<ContainerPorts>, State)>,
selected_panel: SelectablePanel, selected_panel: SelectablePanel,
scroll_title: Option<String>,
sorted_by: Option<(Header, SortedOrder)>, sorted_by: Option<(Header, SortedOrder)>,
status: HashSet<Status>, status: HashSet<Status>,
} }
impl From<&Ui> for FrameData { impl From<&Ui> for FrameData {
fn from(ui: &Ui) -> Self { fn from(ui: &Ui) -> Self {
let (app_data, gui_data) = (ui.app_data.lock(), ui.gui_state.lock()); let (mut app_data, gui_data) = (ui.app_data.lock(), ui.gui_state.lock());
let (filter_by, filter_term) = app_data.get_filter(); let (filter_by, filter_term) = app_data.get_filter();
Self { Self {
@@ -317,6 +336,7 @@ impl From<&Ui> for FrameData {
log_title: app_data.get_log_title(), log_title: app_data.get_log_title(),
port_max_lens: app_data.get_longest_port(), port_max_lens: app_data.get_longest_port(),
ports: app_data.get_selected_ports(), ports: app_data.get_selected_ports(),
scroll_title: app_data.get_scroll_title(gui_data.get_screen_width()),
selected_panel: gui_data.get_selected_panel(), selected_panel: gui_data.get_selected_panel(),
sorted_by: app_data.get_sorted(), sorted_by: app_data.get_sorted(),
status: gui_data.get_status(), status: gui_data.get_status(),
+21 -7
View File
@@ -1,21 +1,35 @@
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Debug)] #[derive(Debug)]
pub struct Rerender(AtomicBool); pub struct Rerender {
draw: AtomicBool,
clear: AtomicBool,
}
impl Rerender { impl Rerender {
pub const fn new() -> Self { pub const fn new() -> Self {
Self(AtomicBool::new(true)) Self {
draw: AtomicBool::new(true),
clear: AtomicBool::new(false),
}
} }
pub fn update(&self) { pub fn update_draw(&self) {
self.0.store(true, Ordering::SeqCst); self.draw.store(true, Ordering::SeqCst);
} }
/// Return the value of the self, and set to false pub fn get_clear(&self) -> bool {
pub fn swap(&self) -> bool { self.clear.swap(false, Ordering::SeqCst)
}
pub fn set_clear(&self) {
self.clear.store(true, Ordering::SeqCst);
}
/// Return the value of the draw, and set to false
pub fn swap_draw(&self) -> bool {
match self match self
.0 .draw
.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst) .compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
{ {
Ok(previous_value) => previous_value, Ok(previous_value) => previous_value,