From 1f9062a5cbe9737da7bc0713b50b4ae2406ce6af Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:42:37 +0000 Subject: [PATCH 01/44] chore: dependencies updated --- Cargo.lock | 520 +++++++++++++++++++++++++++++++++++++++++++---------- Cargo.toml | 2 +- 2 files changed, 426 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0bfe203..d704c9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "android-tzdata" @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -55,43 +55,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.90" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "autocfg" @@ -128,9 +128,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bollard" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41711ad46fda47cd701f6908e59d1bd6b9a2b7464c0d0aeab95c6d37096ff8a" +checksum = "a2b63bbe2a4db01f82f388bee3b1bed3ea9c4cc5f1f4f1fa7a4a171f1ddb2cf1" dependencies = [ "base64", "bollard-stubs", @@ -151,7 +151,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror", + "thiserror 2.0.3", "tokio", "tokio-util", "tower-service", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "bollard-stubs" -version = "1.45.0-rc.26.0.1" +version = "1.46.0-rc.27.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7c5415e3a6bc6d3e99eff6268e488fd4ee25e7b28c10f08fa6760bd9de16e4" +checksum = "3a79775fcd9a02681f9508cd6e44f2164db81c29506063b5904757dd82879d96" dependencies = [ "serde", "serde_repr", @@ -211,9 +211,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "shlex", ] @@ -239,9 +239,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -249,9 +249,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -275,15 +275,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "compact_str" @@ -330,6 +330,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "deranged" version = "0.3.11" @@ -340,6 +375,12 @@ dependencies = [ "serde", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "directories" version = "5.0.1" @@ -361,6 +402,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.13.0" @@ -481,9 +533,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" dependencies = [ "allocator-api2", "equivalent", @@ -591,9 +643,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -647,13 +699,148 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.5.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -674,7 +861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] @@ -686,10 +873,14 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "instability" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" +checksum = "b829f37dead9dc39df40c2d3376c179fdfd2ac771f53f55d3c30dc096a3c0c6e" dependencies = [ + "darling", + "indoc", + "pretty_assertions", + "proc-macro2", "quote", "syn", ] @@ -732,9 +923,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libredox" @@ -752,6 +943,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -774,7 +971,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.0", + "hashbrown 0.15.1", ] [[package]] @@ -914,9 +1111,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -940,10 +1137,20 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.88" +name = "pretty_assertions" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -1025,7 +1232,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1036,9 +1243,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags", "errno", @@ -1067,18 +1274,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.211" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac55e59090389fb9f0dd9e0f3c09615afed1d19094284d0b200441f13550793" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.211" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be4f245ce16bc58d57ef2716271d0d4519e0f6defa147f6e081005bcb278ff" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -1207,6 +1414,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -1243,9 +1456,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.82" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -1253,19 +1466,50 @@ dependencies = [ ] [[package]] -name = "thiserror" -version = "1.0.64" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" dependencies = [ "proc-macro2", "quote", @@ -1314,25 +1558,20 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -1445,27 +1684,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -1497,15 +1721,27 @@ checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1647,6 +1883,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -1768,6 +2013,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -1788,3 +2075,46 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index dec1d8e..49abd04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ similar_names = "allow" [dependencies] anyhow = "1.0" -bollard = "0.17" +bollard = "0.18" cansi = "2.2" clap = { version = "4.5", features = ["color", "derive", "unicode"] } crossterm = "0.28" From 2a834d6c2fa4a15124d24ddbd12f667829e148ad Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:39:06 +0000 Subject: [PATCH 02/44] refactor: execute_command() Include Id and DockerCommand in a DockerMessage, used by the execute_command function to reduce duplicated code --- src/app_data/container_state.rs | 10 +-- src/app_data/mod.rs | 57 +++++++------- src/app_error.rs | 4 +- src/docker_data/message.rs | 16 ++-- src/docker_data/mod.rs | 135 ++++++++++++-------------------- src/input_handler/mod.rs | 30 +++---- 6 files changed, 104 insertions(+), 148 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 1dd717a..39fbdc5 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -333,7 +333,7 @@ impl fmt::Display for State { /// Items for the container control list #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DockerControls { +pub enum DockerCommand { Pause, Restart, Start, @@ -342,7 +342,7 @@ pub enum DockerControls { Delete, } -impl DockerControls { +impl DockerCommand { pub const fn get_color(self) -> Color { match self { Self::Pause => Color::Yellow, @@ -366,7 +366,7 @@ impl DockerControls { } } -impl fmt::Display for DockerControls { +impl fmt::Display for DockerCommand { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let disp = match self { Self::Pause => "pause", @@ -577,7 +577,7 @@ impl Logs { pub struct ContainerItem { pub cpu_stats: VecDeque, pub created: u64, - pub docker_controls: StatefulList, + pub docker_controls: StatefulList, pub id: ContainerId, pub image: ContainerImage, pub is_oxker: bool, @@ -620,7 +620,7 @@ impl ContainerItem { state: State, status: ContainerStatus, ) -> Self { - let mut docker_controls = StatefulList::new(DockerControls::gen_vec(state)); + let mut docker_controls = StatefulList::new(DockerCommand::gen_vec(state)); docker_controls.start(); Self { diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index d21c232..9bbda29 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -535,7 +535,7 @@ impl AppData { /// Get the current selected docker command /// So know which command to execute - pub fn selected_docker_controls(&self) -> Option { + pub fn selected_docker_controls(&self) -> Option { self.get_selected_container().and_then(|i| { i.docker_controls.state.selected().and_then(|x| { i.docker_controls @@ -574,15 +574,14 @@ impl AppData { } } - /// Get mutable Option of the currently selected container DockerControls state + /// Get mutable Option of the currently selected container DockerCommand state pub fn get_control_state(&mut self) -> Option<&mut ListState> { self.get_mut_selected_container() .map(|i| &mut i.docker_controls.state) } - /// Get mutable Option of the currently selected container DockerControls items - /// TODO command or control, need a uniform name across the application - pub fn get_control_items(&mut self) -> Option<&mut Vec> { + /// Get mutable Option of the currently selected container DockerConmand items + pub fn get_control_items(&mut self) -> Option<&mut Vec> { self.get_mut_selected_container() .map(|i| &mut i.docker_controls.items) } @@ -855,7 +854,7 @@ impl AppData { item.status = status; }; if item.state != state { - item.docker_controls.items = DockerControls::gen_vec(state); + item.docker_controls.items = DockerCommand::gen_vec(state); // Update the list state, needs to be None if the gen_vec returns an empty vec match state { State::Removing | State::Restarting | State::Unknown => { @@ -1526,7 +1525,7 @@ mod tests { app_data.docker_controls_start(); let result = app_data.selected_docker_controls(); - assert_eq!(result, Some(DockerControls::Pause)); + assert_eq!(result, Some(DockerCommand::Pause)); } #[test] @@ -1539,7 +1538,7 @@ mod tests { app_data.docker_controls_next(); let result = app_data.selected_docker_controls(); - assert_eq!(result, Some(DockerControls::Restart)); + assert_eq!(result, Some(DockerCommand::Restart)); } #[test] @@ -1551,12 +1550,12 @@ mod tests { app_data.docker_controls_end(); let result = app_data.selected_docker_controls(); - assert_eq!(result, Some(DockerControls::Delete)); + assert_eq!(result, Some(DockerCommand::Delete)); // Next has no effect when at end app_data.docker_controls_next(); let result = app_data.selected_docker_controls(); - assert_eq!(result, Some(DockerControls::Delete)); + assert_eq!(result, Some(DockerCommand::Delete)); } #[test] @@ -1569,19 +1568,19 @@ mod tests { app_data.docker_controls_previous(); let result = app_data.selected_docker_controls(); - assert_eq!(result, Some(DockerControls::Stop)); + assert_eq!(result, Some(DockerCommand::Stop)); // previous has no effect when at start app_data.docker_controls_start(); app_data.docker_controls_previous(); let result = app_data.selected_docker_controls(); - assert_eq!(result, Some(DockerControls::Pause)); + assert_eq!(result, Some(DockerCommand::Pause)); } #[test] /// DockerCommands get correct controls dependant on container state fn test_app_data_get_control_items() { - let test_state = |state: State, expected: &mut Vec| { + let test_state = |state: State, expected: &mut Vec| { let gen_item_state = |state: State| { ContainerItem::new( 1, @@ -1605,42 +1604,42 @@ mod tests { test_state( State::Dead, &mut vec![ - DockerControls::Start, - DockerControls::Restart, - DockerControls::Delete, + DockerCommand::Start, + DockerCommand::Restart, + DockerCommand::Delete, ], ); test_state( State::Exited, &mut vec![ - DockerControls::Start, - DockerControls::Restart, - DockerControls::Delete, + DockerCommand::Start, + DockerCommand::Restart, + DockerCommand::Delete, ], ); test_state( State::Paused, &mut vec![ - DockerControls::Resume, - DockerControls::Stop, - DockerControls::Delete, + DockerCommand::Resume, + DockerCommand::Stop, + DockerCommand::Delete, ], ); - test_state(State::Removing, &mut vec![DockerControls::Delete]); + test_state(State::Removing, &mut vec![DockerCommand::Delete]); test_state( State::Restarting, - &mut vec![DockerControls::Stop, DockerControls::Delete], + &mut vec![DockerCommand::Stop, DockerCommand::Delete], ); test_state( State::Running(RunningState::Healthy), &mut vec![ - DockerControls::Pause, - DockerControls::Restart, - DockerControls::Stop, - DockerControls::Delete, + DockerCommand::Pause, + DockerCommand::Restart, + DockerCommand::Stop, + DockerCommand::Delete, ], ); - test_state(State::Unknown, &mut vec![DockerControls::Delete]); + test_state(State::Unknown, &mut vec![DockerCommand::Delete]); } // ****** // diff --git a/src/app_error.rs b/src/app_error.rs index ba0d66f..a4a6315 100644 --- a/src/app_error.rs +++ b/src/app_error.rs @@ -1,11 +1,11 @@ -use crate::app_data::DockerControls; +use crate::app_data::DockerCommand; use std::fmt; /// app errors to set in global state #[allow(unused)] #[derive(Debug, Clone, Copy)] pub enum AppError { - DockerCommand(DockerControls), + DockerCommand(DockerCommand), DockerExec, DockerLogs, DockerConnect, diff --git a/src/docker_data/message.rs b/src/docker_data/message.rs index 866aada..1f60261 100644 --- a/src/docker_data/message.rs +++ b/src/docker_data/message.rs @@ -1,19 +1,21 @@ use std::sync::Arc; -use crate::app_data::ContainerId; +use crate::app_data::{ContainerId, DockerCommand}; use bollard::Docker; use tokio::sync::oneshot::Sender; #[derive(Debug)] pub enum DockerMessage { ConfirmDelete(ContainerId), - Delete(ContainerId), + Control((DockerCommand, ContainerId)), + + // Delete(ContainerId), Exec(Sender>), - Pause(ContainerId), + // Pause(ContainerId), Quit, - Restart(ContainerId), - Start(ContainerId), - Stop(ContainerId), - Resume(ContainerId), + // Restart(ContainerId), + // Start(ContainerId), + // Stop(ContainerId), + // Resume(ContainerId), Update, } diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index b97102c..b173cae 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -22,7 +22,7 @@ use tokio::{ use uuid::Uuid; use crate::{ - app_data::{AppData, ContainerId, ContainerStatus, DockerControls, State}, + app_data::{AppData, ContainerId, ContainerStatus, DockerCommand, State}, app_error::AppError, parse_args::CliArgs, ui::{GuiState, Status}, @@ -340,7 +340,7 @@ impl DockerData { /// Set the global error as the docker error, and set gui_state to error fn set_error( app_data: &Arc>, - error: DockerControls, + error: DockerCommand, gui_state: &Arc>, ) { app_data @@ -348,99 +348,60 @@ impl DockerData { .set_error(AppError::DockerCommand(error), gui_state, Status::Error); } + /// Execute docker comamnds (start, stop etc) on it's own tokio thread + async fn execute_command(&mut self, control: DockerCommand, id: ContainerId) { + let (app_data, docker, gui_state) = ( + Arc::clone(&self.app_data), + Arc::clone(&self.docker), + Arc::clone(&self.gui_state), + ); + tokio::spawn(async move { + let uuid = Uuid::new_v4(); + GuiState::start_loading_animation(&gui_state, uuid); + if match control { + DockerCommand::Delete => { + docker + .remove_container( + id.get(), + Some(RemoveContainerOptions { + v: false, + force: true, + link: false, + }), + ) + .await + } + DockerCommand::Pause => docker.pause_container(id.get()).await, + DockerCommand::Restart => docker.restart_container(id.get(), None).await, + DockerCommand::Resume => docker.unpause_container(id.get()).await, + DockerCommand::Start => { + docker + .start_container(id.get(), None::>) + .await + } + DockerCommand::Stop => docker.stop_container(id.get(), None).await, + } + .is_err() + { + Self::set_error(&app_data, control, &gui_state); + } + gui_state.lock().stop_loading_animation(uuid); + }); + self.update_everything().await; + } + /// Handle incoming messages, container controls & all container information update /// Spawn Docker commands off into own thread - #[allow(clippy::too_many_lines)] async fn message_handler(&mut self) { while let Some(message) = self.receiver.recv().await { - let docker = Arc::clone(&self.docker); - let gui_state = Arc::clone(&self.gui_state); - let app_data = Arc::clone(&self.app_data); - let uuid = Uuid::new_v4(); - // TODO need to refactor these match message { - DockerMessage::Exec(docker_tx) => { - docker_tx.send(Arc::clone(&self.docker)).ok(); - } - DockerMessage::Pause(id) => { - tokio::spawn(async move { - GuiState::start_loading_animation(&gui_state, uuid); - if docker.pause_container(id.get()).await.is_err() { - Self::set_error(&app_data, DockerControls::Pause, &gui_state); - } - gui_state.lock().stop_loading_animation(uuid); - }); - self.update_everything().await; - } - DockerMessage::Restart(id) => { - tokio::spawn(async move { - GuiState::start_loading_animation(&gui_state, uuid); - if docker.restart_container(id.get(), None).await.is_err() { - Self::set_error(&app_data, DockerControls::Restart, &gui_state); - } - gui_state.lock().stop_loading_animation(uuid); - }); - self.update_everything().await; - } - DockerMessage::Start(id) => { - tokio::spawn(async move { - GuiState::start_loading_animation(&gui_state, uuid); - if docker - .start_container(id.get(), None::>) - .await - .is_err() - { - Self::set_error(&app_data, DockerControls::Start, &gui_state); - } - gui_state.lock().stop_loading_animation(uuid); - }); - self.update_everything().await; - } - DockerMessage::Stop(id) => { - tokio::spawn(async move { - GuiState::start_loading_animation(&gui_state, uuid); - if docker.stop_container(id.get(), None).await.is_err() { - Self::set_error(&app_data, DockerControls::Stop, &gui_state); - } - gui_state.lock().stop_loading_animation(uuid); - }); - self.update_everything().await; - } - DockerMessage::Resume(id) => { - tokio::spawn(async move { - GuiState::start_loading_animation(&gui_state, uuid); - if docker.unpause_container(id.get()).await.is_err() { - Self::set_error(&app_data, DockerControls::Resume, &gui_state); - } - gui_state.lock().stop_loading_animation(uuid); - }); - self.update_everything().await; - } - DockerMessage::Delete(id) => { - tokio::spawn(async move { - GuiState::start_loading_animation(&gui_state, uuid); - if docker - .remove_container( - id.get(), - Some(RemoveContainerOptions { - v: false, - force: true, - link: false, - }), - ) - .await - .is_err() - { - Self::set_error(&app_data, DockerControls::Stop, &gui_state); - } - gui_state.lock().stop_loading_animation(uuid); - }); - self.update_everything().await; - self.gui_state.lock().set_delete_container(None); - } DockerMessage::ConfirmDelete(id) => { self.gui_state.lock().set_delete_container(Some(id)); } + DockerMessage::Control((command, id)) => self.execute_command(command, id).await, + DockerMessage::Exec(docker_tx) => { + docker_tx.send(Arc::clone(&self.docker)).ok(); + } DockerMessage::Update => self.update_everything().await, DockerMessage::Quit => { self.spawns diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index 8228f59..9acefb8 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -22,7 +22,7 @@ use uuid::Uuid; mod message; use crate::{ - app_data::{AppData, DockerControls, Header}, + app_data::{AppData, DockerCommand, Header}, app_error::AppError, docker_data::DockerMessage, exec::{tty_readable, ExecMode}, @@ -112,7 +112,10 @@ impl InputHandler { async fn confirm_delete(&self) { let id = self.gui_state.lock().get_delete_container(); if let Some(id) = id { - self.docker_tx.send(DockerMessage::Delete(id)).await.ok(); + self.docker_tx + .send(DockerMessage::Control((DockerCommand::Delete, id))) + .await + .ok(); } } @@ -281,26 +284,17 @@ impl InputHandler { let option_id = self.app_data.lock().get_selected_container_id(); if let Some(id) = option_id { match command { - DockerControls::Delete => self + DockerCommand::Delete => self .docker_tx .send(DockerMessage::ConfirmDelete(id)) .await .ok(), - DockerControls::Pause => { - self.docker_tx.send(DockerMessage::Pause(id)).await.ok() - } - DockerControls::Resume => { - self.docker_tx.send(DockerMessage::Resume(id)).await.ok() - } - DockerControls::Start => { - self.docker_tx.send(DockerMessage::Start(id)).await.ok() - } - DockerControls::Stop => { - self.docker_tx.send(DockerMessage::Stop(id)).await.ok() - } - DockerControls::Restart => { - self.docker_tx.send(DockerMessage::Restart(id)).await.ok() - } + + _ => self + .docker_tx + .send(DockerMessage::Control((command, id))) + .await + .ok(), }; } } From 5106a01f3dcb87ce5a8f1fb7bf49dc6b3c25d03e Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:48:39 +0000 Subject: [PATCH 03/44] fix: calculate_usage overflow Remove overflow path from calculate_usage, update & simplify tests for this function --- src/docker_data/mod.rs | 297 ++++++++++++++++++++++++++++++----------- 1 file changed, 217 insertions(+), 80 deletions(-) diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index b173cae..bbb35a3 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -71,24 +71,30 @@ pub struct DockerData { impl DockerData { /// Use docker stats to calculate current cpu usage #[allow(clippy::cast_precision_loss)] - // TODO FIX: this can overflow fn calculate_usage(stats: &Stats) -> f64 { let mut cpu_percentage = 0.0; - let previous_cpu = stats.precpu_stats.cpu_usage.total_usage; - let cpu_delta = stats.cpu_stats.cpu_usage.total_usage as f64 - previous_cpu as f64; + 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)) = ( stats.cpu_stats.system_cpu_usage, stats.precpu_stats.system_cpu_usage, ) { - let system_delta = (cpu_stats_usage - 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(|| { - stats - .cpu_stats - .cpu_usage - .percpu_usage - .as_ref() - .map_or(0, std::vec::Vec::len) as u64 + u64::try_from( + stats + .cpu_stats + .cpu_usage + .percpu_usage + .as_ref() + .map_or(0, std::vec::Vec::len), + ) + .unwrap_or_default() }) as f64; if system_delta > 0.0 && cpu_delta > 0.0 { cpu_percentage = (cpu_delta / system_delta) * online_cpus * 100.0; @@ -460,19 +466,19 @@ impl DockerData { // tests, use redis-test container, check logs exists, and selector of logs, and that it increases, and matches end, when you run restart on the docker containers #[cfg(test)] +#[allow(clippy::float_cmp)] mod tests { use bollard::container::{ - BlkioStats, CPUStats, CPUUsage, MemoryStats, PidsStats, StorageStats, ThrottlingData, + BlkioStats, CPUStats, CPUUsage, MemoryStats, PidsStats, Stats, StorageStats, ThrottlingData, }; use super::*; - #[allow(clippy::too_many_lines)] - fn gen_stats(x: u64, y: u64) -> Stats { + fn gen_stats() -> Stats { Stats { read: String::new(), preread: String::new(), - num_procs: 0, + num_procs: 1, pids_stats: PidsStats { current: None, limit: None, @@ -503,33 +509,12 @@ mod tests { }, cpu_stats: CPUStats { cpu_usage: CPUUsage { - percpu_usage: Some(vec![ - 291_593_800, - 182_192_900, - 195_048_700, - 23_032_300, - 132_928_700, - 235_555_600, - 120_225_700, - 175_752_000, - 213_060_300, - 95_321_600, - 226_821_000, - 0, - 109_151_300, - 0, - 86_240_200, - 1_884_400, - 59_077_300, - 23_224_900, - 95_386_300, - 144_987_400, - ]), - total_usage: 250_000_000, - usage_in_usermode: 1_020_000_000, - usage_in_kernelmode: 1_030_000_000, + percpu_usage: Some(vec![50]), + usage_in_usermode: 10, + total_usage: 100, + usage_in_kernelmode: 20, }, - system_cpu_usage: Some(x), + system_cpu_usage: Some(400), online_cpus: Some(1), throttling_data: ThrottlingData { periods: 0, @@ -539,33 +524,12 @@ mod tests { }, precpu_stats: CPUStats { cpu_usage: CPUUsage { - percpu_usage: Some(vec![ - 291_593_800, - 182_192_900, - 195_048_700, - 23_032_300, - 132_928_700, - 235_555_600, - 120_225_700, - 175_752_000, - 213_060_300, - 95_321_600, - 226_821_000, - 0, - 109_151_300, - 0, - 86_240_200, - 1_884_400, - 59_077_300, - 23_224_900, - 93_831_100, - 144_987_400, - ]), - total_usage: 200_000_000, - usage_in_usermode: 1_020_000_000, - usage_in_kernelmode: 1_020_000_000, + percpu_usage: Some(vec![50]), + usage_in_usermode: 10, + total_usage: 100, + usage_in_kernelmode: 20, }, - system_cpu_usage: Some(y), + system_cpu_usage: Some(400), online_cpus: Some(1), throttling_data: ThrottlingData { periods: 0, @@ -579,25 +543,198 @@ mod tests { write_count_normalized: None, write_size_bytes: None, }, - name: "/container_1".to_owned(), - id: "1".to_owned(), + name: String::new(), + id: String::new(), } } #[test] - #[allow(clippy::float_cmp)] - /// Test the stats calculator, had to cheat here to get round input/outputs - fn test_calculate_usage_no_previous_cpu() { - let stats = gen_stats(1_000_000_000, 900_000_000); - let result = DockerData::calculate_usage(&stats); - assert_eq!(result, 50.0); + fn test_calculate_usage_50() { + let mut stats = gen_stats(); + stats.precpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![50]), + usage_in_usermode: 10, + total_usage: 100, + usage_in_kernelmode: 20, + }, + system_cpu_usage: Some(400), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; + stats.cpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![150]), + usage_in_usermode: 20, + total_usage: 150, + usage_in_kernelmode: 30, + }, + system_cpu_usage: Some(500), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; + let cpu_percentage = DockerData::calculate_usage(&stats); + assert_eq!(50.0, cpu_percentage); + } - let stats = gen_stats(1_000_000_000, 800_000_000); - let result = DockerData::calculate_usage(&stats); - assert_eq!(result, 25.0); + #[test] + fn test_calculate_usage_25() { + let mut stats = gen_stats(); + stats.precpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![50]), + usage_in_usermode: 10, + total_usage: 100, + usage_in_kernelmode: 20, + }, + system_cpu_usage: Some(400), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; + stats.cpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![75]), + usage_in_usermode: 20, + total_usage: 125, + usage_in_kernelmode: 30, + }, + system_cpu_usage: Some(500), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; - let stats = gen_stats(1_000_000_000, 750_000_000); - let result = DockerData::calculate_usage(&stats); - assert_eq!(result, 20.00); + let cpu_percentage = DockerData::calculate_usage(&stats); + assert_eq!(25.0, cpu_percentage); + } + + #[test] + fn test_calculate_usage_75() { + let mut stats = gen_stats(); + stats.precpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![50]), + usage_in_usermode: 10, + total_usage: 100, + usage_in_kernelmode: 20, + }, + system_cpu_usage: Some(400), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; + + stats.cpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![175]), + usage_in_usermode: 20, + total_usage: 175, + usage_in_kernelmode: 30, + }, + system_cpu_usage: Some(500), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; + + let cpu_percentage = DockerData::calculate_usage(&stats); + assert_eq!(75.0, cpu_percentage); + } + + #[test] + fn test_calculate_usage_100() { + let mut stats = gen_stats(); + stats.precpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![50]), + usage_in_usermode: 10, + total_usage: 100, + usage_in_kernelmode: 20, + }, + system_cpu_usage: Some(400), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; + stats.cpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![200]), + usage_in_usermode: 20, + total_usage: 200, + usage_in_kernelmode: 30, + }, + system_cpu_usage: Some(500), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; + let cpu_percentage = DockerData::calculate_usage(&stats); + assert_eq!(100.0, cpu_percentage); + } + + #[test] + fn test_calculate_usage_175() { + let mut stats = gen_stats(); + stats.precpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![50]), + usage_in_usermode: 10, + total_usage: 100, + usage_in_kernelmode: 20, + }, + system_cpu_usage: Some(400), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; + + stats.cpu_stats = CPUStats { + cpu_usage: CPUUsage { + percpu_usage: Some(vec![275]), + usage_in_usermode: 20, + total_usage: 275, + usage_in_kernelmode: 30, + }, + system_cpu_usage: Some(500), + online_cpus: Some(1), + throttling_data: ThrottlingData { + periods: 0, + throttled_periods: 0, + throttled_time: 0, + }, + }; + + let cpu_percentage = DockerData::calculate_usage(&stats); + assert_eq!(175.0, cpu_percentage); } } From f0b1145651625ad4e577d79baaf902d4d3bc0579 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:12:54 +0000 Subject: [PATCH 04/44] refactor: pointless code removed --- src/docker_data/mod.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index bbb35a3..06447d0 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -308,12 +308,13 @@ impl DockerData { .lock() .entry(SpawnId::Log(container.id.clone())) .or_insert_with(|| { - // MAYBE make a struct that can create this data? - let app_data = Arc::clone(&self.app_data); - let docker = Arc::clone(&self.docker); - let id = container.id.clone(); - let spawns = Arc::clone(&self.spawns); - tokio::spawn(Self::update_log(app_data, docker, id, last_updated, spawns)) + tokio::spawn(Self::update_log( + Arc::clone(&self.app_data), + Arc::clone(&self.docker), + container.id.clone(), + last_updated, + Arc::clone(&self.spawns), + )) }); }; self.update_all_container_stats(&all_ids); From 76ccf7c00691f815c3ab0bede838c99252ba84f0 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:30:02 +0000 Subject: [PATCH 05/44] refactor: remove uneccessary is_running load --- src/app_data/container_state.rs | 1 + src/docker_data/message.rs | 8 -------- src/docker_data/mod.rs | 16 +--------------- src/input_handler/mod.rs | 14 ++++---------- src/main.rs | 14 ++------------ 5 files changed, 8 insertions(+), 45 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 39fbdc5..b9b22b5 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -133,6 +133,7 @@ impl ContainerPorts { .count() } + /// Return as tuple of Strings, ip address, private port, and public port pub fn print(&self) -> (String, String, String) { ( self.ip diff --git a/src/docker_data/message.rs b/src/docker_data/message.rs index 1f60261..b0af01a 100644 --- a/src/docker_data/message.rs +++ b/src/docker_data/message.rs @@ -8,14 +8,6 @@ use tokio::sync::oneshot::Sender; pub enum DockerMessage { ConfirmDelete(ContainerId), Control((DockerCommand, ContainerId)), - - // Delete(ContainerId), Exec(Sender>), - // Pause(ContainerId), - Quit, - // Restart(ContainerId), - // Start(ContainerId), - // Stop(ContainerId), - // Resume(ContainerId), Update, } diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 06447d0..cb69fd3 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -10,10 +10,7 @@ use futures_util::StreamExt; use parking_lot::Mutex; use std::{ collections::HashMap, - sync::{ - atomic::{AtomicBool, AtomicUsize}, - Arc, - }, + sync::{atomic::AtomicUsize, Arc}, }; use tokio::{ sync::mpsc::{Receiver, Sender}, @@ -62,7 +59,6 @@ pub struct DockerData { binate: Binate, docker: Arc, gui_state: Arc>, - is_running: Arc, init: Option>, receiver: Receiver, spawns: Arc>>>, @@ -410,14 +406,6 @@ impl DockerData { docker_tx.send(Arc::clone(&self.docker)).ok(); } DockerMessage::Update => self.update_everything().await, - DockerMessage::Quit => { - self.spawns - .lock() - .values() - .for_each(tokio::task::JoinHandle::abort); - self.is_running - .store(false, std::sync::atomic::Ordering::SeqCst); - } } } } @@ -443,7 +431,6 @@ impl DockerData { docker_rx: Receiver, docker_tx: Sender, gui_state: Arc>, - is_running: Arc, ) { let args = app_data.lock().args.clone(); if app_data.lock().get_error().is_none() { @@ -454,7 +441,6 @@ impl DockerData { docker: Arc::new(docker), gui_state, init: Some(Arc::new(AtomicUsize::new(0))), - is_running, receiver: docker_rx, spawns: Arc::new(Mutex::new(HashMap::new())), }; diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index 9acefb8..a736182 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -1,10 +1,7 @@ use std::{ fs::OpenOptions, io::{BufWriter, Write}, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, + sync::{atomic::AtomicBool, Arc}, time::SystemTime, }; @@ -84,9 +81,6 @@ impl InputHandler { } } } - if !self.is_running.load(Ordering::SeqCst) { - break; - } } } @@ -97,12 +91,12 @@ impl InputHandler { /// Send a quit message to docker, to abort all spawns, if an error is returned, set is_running to false here instead /// If gui_status is Error or Init, then just set the is_running to false immediately, for a quicker exit - async fn quit(&self) { + fn quit(&self) { let error_init = self .gui_state .lock() .status_contains(&[Status::Error, Status::Init]); - if error_init || self.docker_tx.send(DockerMessage::Quit).await.is_err() { + if !error_init { self.is_running .store(false, std::sync::atomic::Ordering::SeqCst); } @@ -466,7 +460,7 @@ impl InputHandler { let is_q = || key_code == KeyCode::Char('q') || key_code == KeyCode::Char('Q'); if key_modifier == KeyModifiers::CONTROL && is_c() || is_q() && !contains_filter { // Always just quit on Ctrl + c/C or q/Q, unless in FIlter status active - self.quit().await; + self.quit(); } if contains_error { diff --git a/src/main.rs b/src/main.rs index 81a0639..b13cd59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,7 +52,6 @@ async fn docker_init( docker_rx: Receiver, docker_tx: Sender, gui_state: &Arc>, - is_running: &Arc, host: Option, ) { let connection = host.map_or_else(Docker::connect_with_socket_defaults, |host| { @@ -63,10 +62,9 @@ async fn docker_init( if docker.ping().await.is_ok() { let app_data = Arc::clone(app_data); let gui_state = Arc::clone(gui_state); - let is_running = Arc::clone(is_running); tokio::spawn(DockerData::init( - app_data, docker, docker_rx, docker_tx, gui_state, is_running, + app_data, docker, docker_rx, docker_tx, gui_state, )); } else { app_data @@ -118,15 +116,7 @@ async fn main() { let is_running = Arc::new(AtomicBool::new(true)); let (docker_tx, docker_rx) = tokio::sync::mpsc::channel(32); - docker_init( - &app_data, - docker_rx, - docker_tx.clone(), - &gui_state, - &is_running, - host, - ) - .await; + docker_init(&app_data, docker_rx, docker_tx.clone(), &gui_state, host).await; if args.gui { let (input_tx, input_rx) = tokio::sync::mpsc::channel(32); From 68a6551ed038a36330b2f098112829465a1c3c7a Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:36:32 +0000 Subject: [PATCH 06/44] refactor: rename scheduler to heartbeat --- src/docker_data/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index cb69fd3..cc4101b 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -411,7 +411,7 @@ impl DockerData { } /// Send an update message every x ms, where x is the args.docker_interval - fn scheduler(args: &CliArgs, docker_tx: Sender) { + fn heartbeat(args: &CliArgs, docker_tx: Sender) { let update_duration = std::time::Duration::from_millis(u64::from(args.docker_interval)); let mut now = std::time::Instant::now(); tokio::spawn(async move { @@ -445,7 +445,7 @@ impl DockerData { spawns: Arc::new(Mutex::new(HashMap::new())), }; inner.initialise_container_data().await; - Self::scheduler(&args, docker_tx); + Self::heartbeat(&args, docker_tx); inner.message_handler().await; } } From fe3696e5576739d8b033d9e748b5ea696c4b4e4f Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:44:10 +0000 Subject: [PATCH 07/44] refactor: use check_sub for sleep calculations --- src/docker_data/mod.rs | 5 +++-- src/main.rs | 12 ++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index cc4101b..0e38c1c 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -416,9 +416,10 @@ impl DockerData { let mut now = std::time::Instant::now(); tokio::spawn(async move { loop { - let to_sleep = update_duration.saturating_sub(now.elapsed()); - tokio::time::sleep(to_sleep).await; docker_tx.send(DockerMessage::Update).await.ok(); + if let Some(to_sleep) = update_duration.checked_sub(now.elapsed()) { + tokio::time::sleep(to_sleep).await; + } now = std::time::Instant::now(); } }); diff --git a/src/main.rs b/src/main.rs index b13cd59..e11b334 100644 --- a/src/main.rs +++ b/src/main.rs @@ -124,6 +124,7 @@ async fn main() { Ui::create(app_data, gui_state, input_tx, is_running).await; } else { info!("in debug mode\n"); + let mut now = std::time::Instant::now(); // Debug mode for testing, less pointless now, will display some basic information while is_running.load(Ordering::SeqCst) { let err = app_data.lock().get_error(); @@ -131,10 +132,12 @@ async fn main() { error!("{}", err); process::exit(1); } - tokio::time::sleep(std::time::Duration::from_millis(u64::from( - args.docker_interval, - ))) - .await; + if let Some(Ok(to_sleep)) = u128::from(args.docker_interval) + .checked_sub(now.elapsed().as_millis()) + .map(u64::try_from) + { + tokio::time::sleep(std::time::Duration::from_millis(to_sleep)).await; + } let containers = app_data .lock() .get_container_items() @@ -148,6 +151,7 @@ async fn main() { } println!(); } + now = std::time::Instant::now(); } } } From 2860426d57a4458fcee49a2fd20e8e7bb9e71fb5 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 16 Nov 2024 11:32:33 +0000 Subject: [PATCH 08/44] refactor: help_box closure fn --- src/ui/draw_blocks.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 7423418..17f41ca 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -851,22 +851,19 @@ pub fn help_box(f: &mut Frame) { let name_paragraph = Paragraph::new(name_info.lines) .style(Style::default().bg(Color::Magenta).fg(Color::White)) - .block(Block::default()) .alignment(Alignment::Center); + let style = || Style::default().bg(Color::Magenta).fg(Color::Black); let description_paragraph = Paragraph::new(description_info.lines) - .style(Style::default().bg(Color::Magenta).fg(Color::Black)) - .block(Block::default()) + .style(style()) .alignment(Alignment::Center); let help_paragraph = Paragraph::new(button_info.lines) - .style(Style::default().bg(Color::Magenta).fg(Color::Black)) - .block(Block::default()) + .style(style()) .alignment(Alignment::Left); let final_paragraph = Paragraph::new(final_info.lines) - .style(Style::default().bg(Color::Magenta).fg(Color::Black)) - .block(Block::default()) + .style(style()) .alignment(Alignment::Center); let block = Block::default() From 7bb2bef28d90ebc58da86a0365a1904a0c32dffe Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 16 Nov 2024 13:38:41 +0000 Subject: [PATCH 09/44] refactor: statefulList next/previous --- src/app_data/container_state.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index b9b22b5..246caca 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -172,27 +172,25 @@ impl StatefulList { pub fn next(&mut self) { if !self.items.is_empty() { - let i = match self.state.selected() { - Some(i) => { - if i < self.items.len() - 1 { - i + 1 - } else { - i - } + self.state.select(Some(self.state.selected().map_or(0, |i| { + if i < self.items.len() - 1 { + i + 1 + } else { + i } - None => 0, - }; - self.state.select(Some(i)); + }))); } } pub fn previous(&mut self) { if !self.items.is_empty() { - let i = self - .state - .selected() - .map_or(0, |i| if i == 0 { 0 } else { i - 1 }); - self.state.select(Some(i)); + self.state.select(Some(self.state.selected().map_or(0, |i| { + if i == 0 { + 0 + } else { + i - 1 + } + }))); } } From 2d540b0e2210cc04d73035ec59211ffc739174f6 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 16 Nov 2024 13:40:54 +0000 Subject: [PATCH 10/44] refactor: statefulList get_state_title --- src/app_data/container_state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 246caca..55ea1d6 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -200,11 +200,11 @@ impl StatefulList { String::new() } else { let len = self.items.len(); - let c = self + let count = self .state .selected() .map_or(0, |value| if len > 0 { value + 1 } else { value }); - format!(" {c}/{}", self.items.len()) + format!(" {count}/{len}") } } } From 8b9fe4246865441704ae12dff0938868a4fe6f81 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 16 Nov 2024 14:13:56 +0000 Subject: [PATCH 11/44] refactor: massively speed up docker init process --- src/app_data/mod.rs | 6 ++---- src/docker_data/mod.rs | 40 ++++++++++++++++++---------------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 9bbda29..215a523 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -143,10 +143,10 @@ impl AppData { Self { args, containers: StatefulList::new(vec![]), - hidden_containers: vec![], error: None, - sorted_by: None, filter: Filter::new(), + hidden_containers: vec![], + sorted_by: None, } } @@ -350,7 +350,6 @@ impl AppData { .back() .cmp(&item_ord.1.mem_stats.back()) .then_with(|| item_ord.0.name.get().cmp(item_ord.1.name.get())), - Header::Id => item_ord .0 .id @@ -372,7 +371,6 @@ impl AppData { .tx .cmp(&item_ord.1.tx) .then_with(|| item_ord.0.name.get().cmp(item_ord.1.name.get())), - Header::Name => item_ord .0 .name diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 0e38c1c..ce8bee5 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -59,7 +59,6 @@ pub struct DockerData { binate: Binate, docker: Arc, gui_state: Arc>, - init: Option>, receiver: Receiver, spawns: Arc>>>, } @@ -106,12 +105,11 @@ impl DockerData { app_data: Arc>, docker: Arc, id: ContainerId, - init: Option<(Arc, usize)>, state: State, spawn_id: SpawnId, spawns: Arc>>>, ) { - if state.is_alive() || init.is_some() { + if state.is_alive() { let mut stream = docker .stats( id.get(), @@ -168,9 +166,6 @@ impl DockerData { } } spawns.lock().remove(&spawn_id); - if let Some((target, _)) = init { - target.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - } } /// Update all stats, spawn each container into own tokio::spawn thread @@ -181,7 +176,6 @@ impl DockerData { let spawns = Arc::clone(&self.spawns); let spawn_id = SpawnId::Stats((id.clone(), self.binate)); - let init = self.init.as_ref().map(|i| (Arc::clone(i), all_ids.len())); self.spawns .lock() .entry(spawn_id.clone()) @@ -190,7 +184,6 @@ impl DockerData { app_data, docker, id.clone(), - init, *state, spawn_id, spawns, @@ -257,6 +250,7 @@ impl DockerData { app_data: Arc>, docker: Arc, id: ContainerId, + init: Option>, since: u64, spawns: Arc>>>, ) { @@ -279,18 +273,22 @@ impl DockerData { } spawns.lock().remove(&SpawnId::Log(id.clone())); app_data.lock().update_log_by_id(output, &id); + init.map(|i| i.fetch_add(1, std::sync::atomic::Ordering::SeqCst)); } /// Update all logs, spawn each container into own tokio::spawn thread - fn init_all_logs(&self, all_ids: &[(State, ContainerId)]) { + fn init_all_logs(&self, all_ids: &[(State, ContainerId)], init: &Option>) { for (_, id) in all_ids { - let docker = Arc::clone(&self.docker); - let app_data = Arc::clone(&self.app_data); - let spawns = Arc::clone(&self.spawns); - let key = SpawnId::Log(id.clone()); self.spawns.lock().insert( - key, - tokio::spawn(Self::update_log(app_data, docker, id.clone(), 0, spawns)), + SpawnId::Log(id.clone()), + tokio::spawn(Self::update_log( + Arc::clone(&self.app_data), + Arc::clone(&self.docker), + id.clone(), + init.clone(), + 0, + Arc::clone(&self.spawns), + )), ); } } @@ -308,6 +306,7 @@ impl DockerData { Arc::clone(&self.app_data), Arc::clone(&self.docker), container.id.clone(), + None, last_updated, Arc::clone(&self.spawns), )) @@ -327,14 +326,12 @@ impl DockerData { self.update_all_container_stats(&all_ids); - self.init_all_logs(&all_ids); + let init = Arc::new(AtomicUsize::new(0)); + self.init_all_logs(&all_ids, &Some(Arc::clone(&init))); - while let Some(x) = self.init.as_ref() { + while init.load(std::sync::atomic::Ordering::SeqCst) != all_ids.len() { self.app_data.lock().sort_containers(); - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - if x.load(std::sync::atomic::Ordering::SeqCst) == all_ids.len() { - self.init = None; - } + tokio::time::sleep(std::time::Duration::from_millis(10)).await; } self.gui_state.lock().stop_loading_animation(loading_uuid); self.gui_state.lock().status_del(Status::Init); @@ -441,7 +438,6 @@ impl DockerData { binate: Binate::One, docker: Arc::new(docker), gui_state, - init: Some(Arc::new(AtomicUsize::new(0))), receiver: docker_rx, spawns: Arc::new(Mutex::new(HashMap::new())), }; From 7f4238349525c01ae9fb8b1f6c0946e5364dd55e Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 16 Nov 2024 14:50:25 +0000 Subject: [PATCH 12/44] refactor: input_handler --- src/docker_data/mod.rs | 46 ++++----- src/input_handler/mod.rs | 199 ++++++++++++++++++--------------------- src/main.rs | 35 +++---- 3 files changed, 128 insertions(+), 152 deletions(-) diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index ce8bee5..d3dac77 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -196,7 +196,7 @@ impl DockerData { /// Get all current containers, handle into ContainerItem in the app_data struct rather than here /// Just make sure that items sent are guaranteed to have an id /// If in a containerised runtime, will ignore any container that uses the `/app/oxker` as an entry point, unless the `-s` flag is set - pub async fn update_all_containers(&self) -> Vec<(State, ContainerId)> { + async fn update_all_containers(&self) -> Vec<(State, ContainerId)> { let containers = self .docker .list_containers(Some(ListContainersOptions:: { @@ -293,6 +293,26 @@ impl DockerData { } } + /// Initialize docker container data, before any messages are received + async fn initialise_container_data(&mut self) { + self.gui_state.lock().status_push(Status::Init); + let loading_uuid = Uuid::new_v4(); + GuiState::start_loading_animation(&self.gui_state, loading_uuid); + let all_ids = self.update_all_containers().await; + + self.update_all_container_stats(&all_ids); + + let init = Arc::new(AtomicUsize::new(0)); + self.init_all_logs(&all_ids, &Some(Arc::clone(&init))); + + while init.load(std::sync::atomic::Ordering::SeqCst) != all_ids.len() { + self.app_data.lock().sort_containers(); + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + } + self.gui_state.lock().stop_loading_animation(loading_uuid); + self.gui_state.lock().status_del(Status::Init); + } + /// Update all cpu_mem, and selected container log (if a log update join_handle isn't currently being executed) async fn update_everything(&mut self) { let all_ids = self.update_all_containers().await; @@ -314,27 +334,6 @@ impl DockerData { }; self.update_all_container_stats(&all_ids); self.app_data.lock().sort_containers(); - self.gui_state.lock().stop_loading_animation(Uuid::nil()); - } - - /// Initialize docker container data, before any messages are received - async fn initialise_container_data(&mut self) { - self.gui_state.lock().status_push(Status::Init); - let loading_uuid = Uuid::new_v4(); - GuiState::start_loading_animation(&self.gui_state, loading_uuid); - let all_ids = self.update_all_containers().await; - - self.update_all_container_stats(&all_ids); - - let init = Arc::new(AtomicUsize::new(0)); - self.init_all_logs(&all_ids, &Some(Arc::clone(&init))); - - while init.load(std::sync::atomic::Ordering::SeqCst) != all_ids.len() { - self.app_data.lock().sort_containers(); - tokio::time::sleep(std::time::Duration::from_millis(10)).await; - } - self.gui_state.lock().stop_loading_animation(loading_uuid); - self.gui_state.lock().status_del(Status::Init); } /// Set the global error as the docker error, and set gui_state to error @@ -387,6 +386,7 @@ impl DockerData { } gui_state.lock().stop_loading_animation(uuid); }); + self.update_everything().await; } @@ -423,7 +423,7 @@ impl DockerData { } /// Initialise self, and start the message receiving loop - pub async fn init( + pub async fn start( app_data: Arc>, docker: Docker, docker_rx: Receiver, diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index a736182..6799aaa 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -5,7 +5,7 @@ use std::{ time::SystemTime, }; -use bollard::{container::LogsOptions, Docker}; +use bollard::container::LogsOptions; use cansi::v3::categorise_text; use crossterm::{ event::{DisableMouseCapture, KeyCode, KeyModifiers, MouseButton, MouseEvent, MouseEventKind}, @@ -40,7 +40,7 @@ pub struct InputHandler { impl InputHandler { /// Initialize self, and running the message handling loop - pub async fn init( + pub async fn start( app_data: Arc>, rec: Receiver, docker_tx: Sender, @@ -55,11 +55,11 @@ impl InputHandler { rec, mouse_capture: true, }; - inner.start().await; + inner.message_handler().await; } /// check for incoming messages - async fn start(&mut self) { + async fn message_handler(&mut self) { while let Some(message) = self.rec.recv().await { match message { InputMessages::ButtonPress(key) => self.button_press(key.0, key.1).await, @@ -124,7 +124,7 @@ impl InputHandler { if !is_oxker && tty_readable() { let uuid = Uuid::new_v4(); GuiState::start_loading_animation(&self.gui_state, uuid); - let (sx, rx) = tokio::sync::oneshot::channel::>(); + let (sx, rx) = tokio::sync::oneshot::channel(); self.docker_tx.send(DockerMessage::Exec(sx)).await.ok(); if let Ok(docker) = rx.await { @@ -147,111 +147,101 @@ impl InputHandler { /// Toggle the mouse capture (via input of the 'm' key) fn m_key(&mut self) { + let err = || { + self.app_data.lock().set_error( + AppError::MouseCapture(!self.mouse_capture), + &self.gui_state, + Status::Error, + ); + }; if self.mouse_capture { if execute!(std::io::stdout(), DisableMouseCapture).is_ok() { self.gui_state .lock() .set_info_box("✖ mouse capture disabled"); } else { - self.app_data.lock().set_error( - AppError::MouseCapture(false), - &self.gui_state, - Status::Error, - ); + err(); } } else if Ui::enable_mouse_capture().is_ok() { self.gui_state .lock() .set_info_box("✓ mouse capture enabled"); } else { - self.app_data.lock().set_error( - AppError::MouseCapture(true), - &self.gui_state, - Status::Error, - ); + err(); }; self.mouse_capture = !self.mouse_capture; } /// Save the currently selected containers logs into a `[container_name]_[timestamp].log` file - async fn s_key(&self) { - /// This is the inner workings, *inlined* here to return a Result - async fn save_logs( - app_data: &Arc>, - gui_state: &Arc>, - docker_tx: &Sender, - ) -> Result<(), Box> { - let args = app_data.lock().args.clone(); - let container = app_data.lock().get_selected_container_id_state_name(); - if let Some((id, _, name)) = container { - if let Some(log_path) = args.save_dir { - let (sx, rx) = tokio::sync::oneshot::channel::>(); - docker_tx.send(DockerMessage::Exec(sx)).await?; + async fn save_logs(&self) -> Result<(), Box> { + let args = self.app_data.lock().args.clone(); + let container = self.app_data.lock().get_selected_container_id_state_name(); + if let Some((id, _, name)) = container { + if let Some(log_path) = args.save_dir { + let (sx, rx) = tokio::sync::oneshot::channel(); + self.docker_tx.send(DockerMessage::Exec(sx)).await?; - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .map_or(0, |i| i.as_secs()); + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .map_or(0, |i| i.as_secs()); - let path = log_path.join(format!("{name}_{now}.log")); + let path = log_path.join(format!("{name}_{now}.log")); - let docker = rx.await?; - let options = Some(LogsOptions:: { - stderr: true, - stdout: true, - timestamps: args.timestamp, - since: 0, - ..Default::default() - }); - let mut logs = docker.logs(id.get(), options); - let mut output = vec![]; + let options = Some(LogsOptions:: { + stderr: true, + stdout: true, + timestamps: args.timestamp, + since: 0, + ..Default::default() + }); + let mut logs = rx.await?.logs(id.get(), options); + let mut output = vec![]; - while let Some(Ok(value)) = logs.next().await { - let data = value.to_string(); - if !data.trim().is_empty() { - output.push( - categorise_text(&data) - .into_iter() - .map(|i| i.text) - .collect::(), - ); - } - } - if !output.is_empty() { - let mut stream = BufWriter::new( - OpenOptions::new() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open(&path)?, + while let Some(Ok(value)) = logs.next().await { + let data = value.to_string(); + if !data.trim().is_empty() { + output.push( + categorise_text(&data) + .into_iter() + .map(|i| i.text) + .collect::(), ); - - for line in &output { - stream.write_all(line.as_bytes())?; - } - stream.flush()?; - - gui_state - .lock() - .set_info_box(&format!("saved to {}", path.display())); } } - } - Ok(()) - } + if !output.is_empty() { + let mut stream = BufWriter::new( + OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&path)?, + ); + for line in &output { + stream.write_all(line.as_bytes())?; + } + stream.flush()?; + + self.gui_state + .lock() + .set_info_box(&format!("saved to {}", path.display())); + } + } + } + Ok(()) + } + + /// Attempt to save the currently selected container logs to a file + async fn s_key(&self) { let log_status = Status::Logs; let status = self.gui_state.lock().status_contains(&[log_status]); if !status { self.gui_state.lock().status_push(log_status); - let uuid = Uuid::new_v4(); GuiState::start_loading_animation(&self.gui_state, uuid); - if save_logs(&self.app_data, &self.gui_state, &self.docker_tx) - .await - .is_err() - { + if self.save_logs().await.is_err() { self.app_data.lock().set_error( AppError::DockerLogs, &self.gui_state, @@ -296,50 +286,43 @@ impl InputHandler { } /// 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 fn tab_key(&self) { - let is_containers = - self.gui_state.lock().get_selected_panel() == SelectablePanel::Containers; - let count = if self.app_data.lock().get_container_len() == 0 && is_containers { - 2 - } else { - 1 - }; - for _ in 0..count { + self.gui_state.lock().next_panel(); + if self.app_data.lock().get_container_len() == 0 + && self.gui_state.lock().get_selected_panel() == SelectablePanel::Commands + { self.gui_state.lock().next_panel(); } } /// Change to previously selected panel + /// Need to skip the commands planel if there no are current containers running fn back_tab_key(&self) { - let is_containers = self.gui_state.lock().get_selected_panel() == SelectablePanel::Logs; - let count = if self.app_data.lock().get_container_len() == 0 && is_containers { - 2 - } else { - 1 - }; - for _ in 0..count { + self.gui_state.lock().previous_panel(); + if self.app_data.lock().get_container_len() == 0 + && self.gui_state.lock().get_selected_panel() == SelectablePanel::Commands + { self.gui_state.lock().previous_panel(); } } fn home_key(&self) { - let mut locked_data = self.app_data.lock(); let selected_panel = self.gui_state.lock().get_selected_panel(); match selected_panel { - SelectablePanel::Containers => locked_data.containers_start(), - SelectablePanel::Logs => locked_data.log_start(), - SelectablePanel::Commands => locked_data.docker_controls_start(), + SelectablePanel::Containers => self.app_data.lock().containers_start(), + SelectablePanel::Logs => self.app_data.lock().log_start(), + SelectablePanel::Commands => self.app_data.lock().docker_controls_start(), } } /// Go to end of the list of the currently selected panel fn end_key(&self) { - let mut locked_data = self.app_data.lock(); let selected_panel = self.gui_state.lock().get_selected_panel(); match selected_panel { - SelectablePanel::Containers => locked_data.containers_end(), - SelectablePanel::Logs => locked_data.log_end(), - SelectablePanel::Commands => locked_data.docker_controls_end(), + SelectablePanel::Containers => self.app_data.lock().containers_end(), + SelectablePanel::Logs => self.app_data.lock().log_end(), + SelectablePanel::Commands => self.app_data.lock().docker_controls_end(), } } @@ -525,23 +508,21 @@ impl InputHandler { /// Change state to next, depending which panel is currently in focus fn next(&self) { - let mut locked_data = self.app_data.lock(); let selected_panel = self.gui_state.lock().get_selected_panel(); match selected_panel { - SelectablePanel::Containers => locked_data.containers_next(), - SelectablePanel::Logs => locked_data.log_next(), - SelectablePanel::Commands => locked_data.docker_controls_next(), + SelectablePanel::Containers => self.app_data.lock().containers_next(), + SelectablePanel::Logs => self.app_data.lock().log_next(), + SelectablePanel::Commands => self.app_data.lock().docker_controls_next(), }; } /// Change state to previous, depending which panel is currently in focus fn previous(&self) { - let mut locked_data = self.app_data.lock(); let selected_panel = self.gui_state.lock().get_selected_panel(); match selected_panel { - SelectablePanel::Containers => locked_data.containers_previous(), - SelectablePanel::Logs => locked_data.log_previous(), - SelectablePanel::Commands => locked_data.docker_controls_previous(), + SelectablePanel::Containers => self.app_data.lock().containers_previous(), + SelectablePanel::Logs => self.app_data.lock().log_previous(), + SelectablePanel::Commands => self.app_data.lock().docker_controls_previous(), } } } diff --git a/src/main.rs b/src/main.rs index e11b334..ca23fb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,28 +54,26 @@ async fn docker_init( gui_state: &Arc>, host: Option, ) { + // let err = || let connection = host.map_or_else(Docker::connect_with_socket_defaults, |host| { Docker::connect_with_socket(&host, 120, API_DEFAULT_VERSION) }); if let Ok(docker) = connection { if docker.ping().await.is_ok() { - let app_data = Arc::clone(app_data); - let gui_state = Arc::clone(gui_state); - - tokio::spawn(DockerData::init( - app_data, docker, docker_rx, docker_tx, gui_state, + tokio::spawn(DockerData::start( + Arc::clone(app_data), + docker, + docker_rx, + docker_tx, + Arc::clone(gui_state), )); - } else { - app_data - .lock() - .set_error(AppError::DockerConnect, gui_state, Status::DockerConnect); + return; } - } else { - app_data - .lock() - .set_error(AppError::DockerConnect, gui_state, Status::DockerConnect); } + app_data + .lock() + .set_error(AppError::DockerConnect, gui_state, Status::DockerConnect); } /// Create data for, and then spawn a tokio thread, for the input handler @@ -86,15 +84,12 @@ fn handler_init( input_rx: Receiver, is_running: &Arc, ) { - let app_data = Arc::clone(app_data); - let gui_state = Arc::clone(gui_state); - let is_running = Arc::clone(is_running); - tokio::spawn(input_handler::InputHandler::init( - app_data, + tokio::spawn(input_handler::InputHandler::start( + Arc::clone(app_data), input_rx, docker_sx.clone(), - gui_state, - is_running, + Arc::clone(gui_state), + Arc::clone(is_running), )); } From ba6a95241389f99d504ee4bf3e87e19006f12e49 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:28:27 +0000 Subject: [PATCH 13/44] refactor: dead code removed, methods renamed --- src/app_error.rs | 6 +----- src/parse_args.rs | 7 +------ src/ui/mod.rs | 2 +- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/app_error.rs b/src/app_error.rs index a4a6315..4afcf3f 100644 --- a/src/app_error.rs +++ b/src/app_error.rs @@ -2,15 +2,13 @@ use crate::app_data::DockerCommand; use std::fmt; /// app errors to set in global state -#[allow(unused)] +// #[allow(unused)] #[derive(Debug, Clone, Copy)] pub enum AppError { DockerCommand(DockerCommand), DockerExec, DockerLogs, DockerConnect, - DockerInterval, - InputPoll, MouseCapture(bool), Terminal, } @@ -23,8 +21,6 @@ impl fmt::Display for AppError { Self::DockerExec => write!(f, "Unable to exec into container"), Self::DockerLogs => write!(f, "Unable to save logs"), Self::DockerConnect => write!(f, "Unable to access docker daemon"), - Self::DockerInterval => write!(f, "Docker update interval needs to be greater than 0"), - Self::InputPoll => write!(f, "Unable to poll user input"), Self::MouseCapture(x) => { let reason = if *x { "en" } else { "dis" }; write!(f, "Unable to {reason}able mouse capture") diff --git a/src/parse_args.rs b/src/parse_args.rs index e29d84c..c1cb998 100644 --- a/src/parse_args.rs +++ b/src/parse_args.rs @@ -65,12 +65,7 @@ impl CliArgs { /// An ENV is set in the ./containerised/Dockerfile, if this is ENV found, then sleep for 250ms, else the container, for as yet unknown reasons, will close immediately /// returns a bool, so that the `update_all_containers()` won't bother to check the entry point unless running via a container fn check_if_in_container() -> bool { - if let Ok(value) = std::env::var(ENV_KEY) { - if value == ENV_VALUE { - return true; - } - } - false + std::env::var(ENV_KEY).map_or(false, |i| i == ENV_VALUE) } /// Parse cli arguments diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 2158fef..b0e7fef 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -59,7 +59,7 @@ impl Ui { } /// Create a new Ui struct, and execute the drawing loop - pub async fn create( + pub async fn start( app_data: Arc>, gui_state: Arc>, input_tx: Sender, From d01e0a8588148b0d3274fb304b03bce26d971823 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:08:19 +0000 Subject: [PATCH 14/44] refactor: remove Docker sleep --- src/main.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index ca23fb9..2ab1d17 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,9 +52,9 @@ async fn docker_init( docker_rx: Receiver, docker_tx: Sender, gui_state: &Arc>, - host: Option, ) { - // let err = || + let host = read_docker_host(&app_data.lock().args); + let connection = host.map_or_else(Docker::connect_with_socket_defaults, |host| { Docker::connect_with_socket(&host, 120, API_DEFAULT_VERSION) }); @@ -99,24 +99,17 @@ async fn main() { let args = CliArgs::new(); - // If running via Docker image, need to sleep else program will just quit straight away, no real idea why - // So just sleep for small while - if args.in_container { - std::thread::sleep(std::time::Duration::from_millis(250)); - } - let host = read_docker_host(&args); - let app_data = Arc::new(Mutex::new(AppData::default(args.clone()))); let gui_state = Arc::new(Mutex::new(GuiState::default())); let is_running = Arc::new(AtomicBool::new(true)); let (docker_tx, docker_rx) = tokio::sync::mpsc::channel(32); - docker_init(&app_data, docker_rx, docker_tx.clone(), &gui_state, host).await; + docker_init(&app_data, docker_rx, docker_tx.clone(), &gui_state).await; if args.gui { let (input_tx, input_rx) = tokio::sync::mpsc::channel(32); handler_init(&app_data, &docker_tx, &gui_state, input_rx, &is_running); - Ui::create(app_data, gui_state, input_tx, is_running).await; + Ui::start(app_data, gui_state, input_tx, is_running).await; } else { info!("in debug mode\n"); let mut now = std::time::Instant::now(); From 0c6f53228f01196e352c2069383ba1e7a10950a8 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 19 Nov 2024 08:25:55 +0000 Subject: [PATCH 15/44] fix: update containerised Dockerfile --- containerised/Dockerfile | 14 +++++++------- containerised/target.sh | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 containerised/target.sh diff --git a/containerised/Dockerfile b/containerised/Dockerfile index 3c2c7b8..e05760a 100644 --- a/containerised/Dockerfile +++ b/containerised/Dockerfile @@ -2,7 +2,7 @@ ## Builder ## ############# -FROM --platform=linux/amd64 rust:slim AS builder +FROM --platform=$BUILDPLATFORM rust:slim AS builder ARG TARGETARCH @@ -11,9 +11,9 @@ ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="aarch64-linux-gnu-gcc" ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-lgcc" ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_MUSLEABIHF_LINKER="arm-linux-gnueabihf-ld" -COPY ./containerised/platform.sh . +COPY ./containerised/target.sh . -RUN chmod +x ./platform.sh && ./platform.sh +RUN chmod +x ./target.sh && ./target.sh RUN apt-get update && apt-get install $(cat /.compiler) -y @@ -29,10 +29,10 @@ COPY Cargo.* /usr/src/oxker/ WORKDIR /usr/src/oxker # Install target platform (Cross-Compilation) -RUN rustup target add $(cat /.platform) +RUN rustup target add $(cat /.target) # This is a dummy build to get the dependencies cached - probably not needed - as run via a github action -RUN cargo build --target $(cat /.platform) --release +RUN cargo build --target $(cat /.target) --release # Now copy in the rest of the sources COPY src /usr/src/oxker/src/ @@ -41,9 +41,9 @@ COPY src /usr/src/oxker/src/ RUN touch /usr/src/oxker/src/main.rs # This is the actual application build -RUN cargo build --release --target $(cat /.platform) +RUN cargo build --release --target $(cat /.target) -RUN cp /usr/src/oxker/target/$(cat /.platform)/release/oxker / +RUN cp /usr/src/oxker/target/$(cat /.target)/release/oxker / ############# ## Runtime ## diff --git a/containerised/target.sh b/containerised/target.sh new file mode 100644 index 0000000..40ba3d6 --- /dev/null +++ b/containerised/target.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# Used in Docker build to set platform dependent variables + +case $TARGETARCH in + +"amd64") + echo "x86_64-unknown-linux-musl" >/.target + echo "" >/.compiler + ;; +"arm64") + echo "aarch64-unknown-linux-musl" >/.target + echo "gcc-aarch64-linux-gnu" >/.compiler + ;; +"arm") + echo "arm-unknown-linux-musleabihf" >/.target + echo "gcc-arm-linux-gnueabihf" >/.compiler + ;; +esac From 5ee48d5708fa6de0206c021db0bb611196e66fba Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 19 Nov 2024 08:27:45 +0000 Subject: [PATCH 16/44] refactor: dead code removed --- containerised/platform.sh | 18 ------------------ src/ui/draw_blocks.rs | 3 +-- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 containerised/platform.sh diff --git a/containerised/platform.sh b/containerised/platform.sh deleted file mode 100644 index 9ada7c8..0000000 --- a/containerised/platform.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -# Used in Docker build to set platform dependent variables - -case $TARGETARCH in - -"amd64") - echo "x86_64-unknown-linux-musl" >/.platform - echo "" >/.compiler - ;; -"arm64") - echo "aarch64-unknown-linux-musl" >/.platform - echo "gcc-aarch64-linux-gnu" >/.compiler - ;; -"arm") - echo "arm-unknown-linux-musleabihf" >/.platform - echo "gcc-arm-linux-gnueabihf" >/.compiler - ;; -esac diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 17f41ca..23b8205 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -1000,8 +1000,7 @@ pub fn error(f: &mut Frame, error: AppError, seconds: Option) { let area = popup(lines, max_line_width, f.area(), BoxLocation::MiddleCentre); - // let (paragraph, area) = gen_error(f, error, seconds); - f.render_widget(Clear, area); + f.render_widget(Clear, area); f.render_widget(paragraph, area); } From 751d997a3dac823e144ae62e6c1455676e50ddb8 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:27:47 +0000 Subject: [PATCH 17/44] chore: Rust 1.83 linting --- src/app_data/mod.rs | 8 -------- src/docker_data/mod.rs | 7 ++++--- src/ui/draw_blocks.rs | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 215a523..2907570 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -160,7 +160,6 @@ impl AppData { } /// Filter related methods - /// Get the current filter term pub const fn get_filter_term(&self) -> Option<&String> { self.filter.term.as_ref() @@ -280,7 +279,6 @@ impl AppData { } /// Container sort related methods - /// Change the sorted order, also set the selected container state to match new order fn set_sorted(&mut self, x: Option<(Header, SortedOrder)>) { self.sorted_by = x; @@ -390,7 +388,6 @@ impl AppData { } /// Container state methods - /// Get the total number of none "hidden" containers pub fn get_container_len(&self) -> usize { self.containers.items.len() @@ -530,7 +527,6 @@ impl AppData { } /// Selected DockerCommand methods - /// Get the current selected docker command /// So know which command to execute pub fn selected_docker_controls(&self) -> Option { @@ -585,7 +581,6 @@ impl AppData { } /// Logs related methods - /// Get the title for log panel for selected container, will be either /// 1) "logs x/x - container_name - container_image" /// 2) "logs - container_name - container_image" when no logs found @@ -650,7 +645,6 @@ impl AppData { } /// Chart data related methods - /// Get mutable Option of the currently selected container chart data pub fn get_chart_data(&mut self) -> Option<(CpuTuple, MemTuple)> { self.containers @@ -661,7 +655,6 @@ impl AppData { } /// Error related methods - /// Get single app_state error pub const fn get_error(&self) -> Option { self.error @@ -726,7 +719,6 @@ impl AppData { } /// Update related methods - /// Get mutable reference to a container in the containers vec & the hidden_containers vec fn get_any_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> { if self.get_hidden_container_by_id(id).is_some() { diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index d3dac77..1188b84 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -277,15 +277,16 @@ impl DockerData { } /// Update all logs, spawn each container into own tokio::spawn thread - fn init_all_logs(&self, all_ids: &[(State, ContainerId)], init: &Option>) { + fn init_all_logs(&self, all_ids: &[(State, ContainerId)], init: Option<&Arc>) { for (_, id) in all_ids { + // let init = init.map(|i|Arc::clone(i)); self.spawns.lock().insert( SpawnId::Log(id.clone()), tokio::spawn(Self::update_log( Arc::clone(&self.app_data), Arc::clone(&self.docker), id.clone(), - init.clone(), + init.map(Arc::clone), 0, Arc::clone(&self.spawns), )), @@ -303,7 +304,7 @@ impl DockerData { self.update_all_container_stats(&all_ids); let init = Arc::new(AtomicUsize::new(0)); - self.init_all_logs(&all_ids, &Some(Arc::clone(&init))); + self.init_all_logs(&all_ids, Some(&init)); while init.load(std::sync::atomic::Ordering::SeqCst) != all_ids.len() { self.app_data.lock().sort_containers(); diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 23b8205..395bf31 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -1000,7 +1000,7 @@ pub fn error(f: &mut Frame, error: AppError, seconds: Option) { let area = popup(lines, max_line_width, f.area(), BoxLocation::MiddleCentre); - f.render_widget(Clear, area); + f.render_widget(Clear, area); f.render_widget(paragraph, area); } From b78713579c4706d605e5b35fcd832610a0152294 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:28:32 +0000 Subject: [PATCH 18/44] chore: dependencies updated --- Cargo.lock | 92 +++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d704c9d..0cdf7dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,9 +128,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bollard" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b63bbe2a4db01f82f388bee3b1bed3ea9c4cc5f1f4f1fa7a4a171f1ddb2cf1" +checksum = "97ccca1260af6a459d75994ad5acc1651bcabcbdbc41467cc9786519ab854c30" dependencies = [ "base64", "bollard-stubs", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "bollard-stubs" -version = "1.46.0-rc.27.3.1" +version = "1.47.1-rc.27.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a79775fcd9a02681f9508cd6e44f2164db81c29506063b5904757dd82879d96" +checksum = "3f179cfbddb6e77a5472703d4b30436bff32929c0aa8a9008ecf23d1d3cdd0da" dependencies = [ "serde", "serde_repr", @@ -184,9 +184,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cansi" @@ -533,9 +533,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "equivalent", @@ -608,9 +608,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", @@ -861,7 +861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", "serde", ] @@ -902,9 +902,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" @@ -923,9 +923,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.162" +version = "0.2.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" [[package]] name = "libredox" @@ -945,9 +945,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" @@ -971,7 +971,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.1", + "hashbrown 0.15.2", ] [[package]] @@ -1148,9 +1148,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1243,9 +1243,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags", "errno", @@ -1294,9 +1294,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -1406,9 +1406,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1456,9 +1456,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -1617,9 +1617,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1628,9 +1628,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -1639,9 +1639,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -1686,9 +1686,9 @@ checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" @@ -1721,9 +1721,9 @@ checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -2033,9 +2033,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -2045,9 +2045,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", @@ -2078,18 +2078,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", From b05c9ad8953e0ee3b0c14654f344c6ce3f1442c0 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:33:33 +0000 Subject: [PATCH 19/44] docs: changelog --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 858c2c6..38447fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +### Chores ++ dependencies updated, [b78713579c4706d605e5b35fcd832610a0152294] ++ Rust 1.83 linting, [751d997a3dac823e144ae62e6c1455676e50ddb8] + +### Fixes ++ update containerised Dockerfile, [0c6f53228f01196e352c2069383ba1e7a10950a8] ++ calculate_usage overflow, [5106a01f3dcb87ce5a8f1fb7bf49dc6b3c25d03e] + +### Refactors ++ massively speed up docker init process, [8b9fe4246865441704ae12dff0938868a4fe6f81] ++ remove docker sleep, [f1562d1084336fe5be39894c93cb49107f0a4a6d] ++ dead code removed, [5ee48d5708fa6de0206c021db0bb611196e66fba], [ba6a95241389f99d504ee4bf3e87e19006f12e49], [f0b1145651625ad4e577d79baaf902d4d3bc0579] ++ input_handler, [7f4238349525c01ae9fb8b1f6c0946e5364dd55e] ++ statefulList get_state_title, [2d540b0e2210cc04d73035ec59211ffc739174f6] ++ statefulList next/previous, [7bb2bef28d90ebc58da86a0365a1904a0c32dffe] ++ help_box closure fn, [2860426d57a4458fcee49a2fd20e8e7bb9e71fb5] ++ use check_sub for sleep calculations, [fe3696e5576739d8b033d9e748b5ea696c4b4e4f] ++ rename scheduler to heartbeat, [68a6551ed038a36330b2f098112829465a1c3c7a] ++ remove uneccessary is_running load, [76ccf7c00691f815c3ab0bede838c99252ba84f0] ++ execute_command(), [2a834d6c2fa4a15124d24ddbd12f667829e148ad] + + # v0.8.0 ### 2024-10-22 From c6200e8f77f8bb1f0152cb9374029d15cc45df9d Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:25:04 +0000 Subject: [PATCH 20/44] Chore: dependencies updated --- Cargo.lock | 82 +++++++++++++++++++++++++----------------------------- Cargo.toml | 2 +- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0cdf7dd..ac8d64f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -19,9 +19,9 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -211,9 +211,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" dependencies = [ "shlex", ] @@ -427,12 +427,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -548,12 +548,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hex" version = "0.4.3" @@ -856,9 +850,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -908,10 +902,11 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -923,9 +918,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.166" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libredox" @@ -991,11 +986,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi", "libc", "log", "wasi", @@ -1337,7 +1331,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_derive", "serde_json", @@ -1456,9 +1450,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.89" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -1528,9 +1522,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -1549,9 +1543,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -1569,9 +1563,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -1660,9 +1654,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -1781,9 +1775,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" dependencies = [ "cfg-if", "once_cell", @@ -1792,9 +1786,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" dependencies = [ "bumpalo", "log", @@ -1807,9 +1801,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1817,9 +1811,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" dependencies = [ "proc-macro2", "quote", @@ -1830,9 +1824,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "winapi" diff --git a/Cargo.toml b/Cargo.toml index 49abd04..90b99c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ directories = "5.0" futures-util = "0.3" parking_lot = { version = "0.12" } ratatui = "0.29" -tokio = { version = "1.41", features = ["full"] } +tokio = { version = "1.42", features = ["full"] } tokio-util = "0.7" tracing = "0.1" tracing-subscriber = "0.3" From c739637b91c8fa742a69f4d888678d7b3964678c Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:01:56 +0000 Subject: [PATCH 21/44] feat: `--no-stderr` cli arg, closes #52 New cli argument which will remove stderr output from logs, defaults to false --- CHANGELOG.md | 2 +- README.md | 1 + src/app_data/mod.rs | 1 + src/docker_data/mod.rs | 7 +++++-- src/main.rs | 1 + src/parse_args.rs | 12 +++++++++--- src/ui/mod.rs | 1 + 7 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38447fa..bbbe1bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ + help_box closure fn, [2860426d57a4458fcee49a2fd20e8e7bb9e71fb5] + use check_sub for sleep calculations, [fe3696e5576739d8b033d9e748b5ea696c4b4e4f] + rename scheduler to heartbeat, [68a6551ed038a36330b2f098112829465a1c3c7a] -+ remove uneccessary is_running load, [76ccf7c00691f815c3ab0bede838c99252ba84f0] ++ remove unnecessary is_running load, [76ccf7c00691f815c3ab0bede838c99252ba84f0] + execute_command(), [2a834d6c2fa4a15124d24ddbd12f667829e148ad] diff --git a/README.md b/README.md index 49c2938..b239e57 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ Available command line arguments |```-s```| If running via Docker, will display the oxker container.| |```-g```| No TUI, essentially a debugging mode with limited functionality, for now.| |```--host [string]```| Connect to Docker with a custom hostname. Defaults to `/var/run/docker.sock`. Will use `$DOCKER_HOST` environment variable if set.| +|```--no-stderr```| Do not include stderr output in logs.| |```--save-dir [string]```| Save exported logs into a custom directory. Defaults to `$HOME`.| |```--use-cli```| Use the Docker application when exec-ing into a container, instead of the Docker API.| diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 2907570..f0f05f1 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -442,6 +442,7 @@ impl AppData { } /// Find the longest port when it's transformed into a string, defaults are header lens (ip, private, public) + /// TODO refactor this, and write comments as to whete the initial sizes come from pub fn get_longest_port(&self) -> (usize, usize, usize) { let mut longest_ip = 5; let mut longest_private = 10; diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 1188b84..9a42b99 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -253,10 +253,11 @@ impl DockerData { init: Option>, since: u64, spawns: Arc>>>, + stderr: bool, ) { let options = Some(LogsOptions:: { stdout: true, - stderr: true, + stderr, timestamps: true, since: i64::try_from(since).unwrap_or_default(), ..Default::default() @@ -289,6 +290,7 @@ impl DockerData { init.map(Arc::clone), 0, Arc::clone(&self.spawns), + self.args.std_err, )), ); } @@ -330,6 +332,7 @@ impl DockerData { None, last_updated, Arc::clone(&self.spawns), + self.args.std_err, )) }); }; @@ -348,7 +351,7 @@ impl DockerData { .set_error(AppError::DockerCommand(error), gui_state, Status::Error); } - /// Execute docker comamnds (start, stop etc) on it's own tokio thread + /// Execute docker commands (start, stop etc) on it's own tokio thread async fn execute_command(&mut self, control: DockerCommand, id: ContainerId) { let (app_data, docker, gui_state) = ( Arc::clone(&self.app_data), diff --git a/src/main.rs b/src/main.rs index 2ab1d17..3ad0af4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -164,6 +164,7 @@ mod tests { docker_interval: 1000, gui: true, host: None, + std_err: false, in_container: false, save_dir: None, raw: false, diff --git a/src/parse_args.rs b/src/parse_args.rs index c1cb998..911ba20 100644 --- a/src/parse_args.rs +++ b/src/parse_args.rs @@ -37,13 +37,17 @@ pub struct Args { #[clap(long, short = None)] pub host: Option, - /// Force use of docker cli when execing into containers - #[clap(long="use-cli", short = None)] - pub use_cli: bool, + /// Do not include stderr output in logs + #[clap(long = "no-stderr")] + pub no_std_err: bool, /// Directory for saving exported logs, defaults to `$HOME` #[clap(long="save-dir", short = None)] pub save_dir: Option, + + /// Force use of docker cli when execing into containers + #[clap(long="use-cli", short = None)] + pub use_cli: bool, } #[derive(Debug, Clone)] @@ -58,6 +62,7 @@ pub struct CliArgs { pub raw: bool, pub show_self: bool, pub timestamp: bool, + pub std_err: bool, pub use_cli: bool, } @@ -92,6 +97,7 @@ impl CliArgs { in_container: Self::check_if_in_container(), save_dir: logs_dir, raw: args.raw, + std_err: !args.no_std_err, show_self: !args.show_self, timestamp: !args.timestamp, } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index b0e7fef..9c209e0 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -220,6 +220,7 @@ impl Ui { } /// Frequent data required by multiple framde drawing functions, can reduce mutex reads by placing it all in here +/// TODO add more items to this, and split up into parts #[derive(Debug)] pub struct FrameData { columns: Columns, From 049bb350be700b414f5f8b28ca839351294b9686 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:03:07 +0000 Subject: [PATCH 22/44] docs: changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbbe1bf..fb7eb23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ ### Chores -+ dependencies updated, [b78713579c4706d605e5b35fcd832610a0152294] ++ dependencies updated, [b78713579c4706d605e5b35fcd832610a0152294], [c6200e8f77f8bb1f0152cb9374029d15cc45df9d] + Rust 1.83 linting, [751d997a3dac823e144ae62e6c1455676e50ddb8] +### Features ++ `--no-stderr` cli arg, closes #52, [c739637b91c8fa742a69f4d888678d7b3964678c] + ### Fixes + update containerised Dockerfile, [0c6f53228f01196e352c2069383ba1e7a10950a8] + calculate_usage overflow, [5106a01f3dcb87ce5a8f1fb7bf49dc6b3c25d03e] From de72ab47cda2f9cf76e70556173818dd896a67d6 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:03:07 +0000 Subject: [PATCH 23/44] docs: changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbbe1bf..fb7eb23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ ### Chores -+ dependencies updated, [b78713579c4706d605e5b35fcd832610a0152294] ++ dependencies updated, [b78713579c4706d605e5b35fcd832610a0152294], [c6200e8f77f8bb1f0152cb9374029d15cc45df9d] + Rust 1.83 linting, [751d997a3dac823e144ae62e6c1455676e50ddb8] +### Features ++ `--no-stderr` cli arg, closes #52, [c739637b91c8fa742a69f4d888678d7b3964678c] + ### Fixes + update containerised Dockerfile, [0c6f53228f01196e352c2069383ba1e7a10950a8] + calculate_usage overflow, [5106a01f3dcb87ce5a8f1fb7bf49dc6b3c25d03e] From e5927f781a7e9517b9fa00a2d1a835d2774a9d26 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:37:38 +0000 Subject: [PATCH 24/44] refactor: Remove numerous clones() get_container_name_by_id clone, init_all_log refactor to remove clones, remove id.clone() by using SpawnId.get_id() --- src/app_data/container_state.rs | 9 ++- src/app_data/mod.rs | 44 +++++++------ src/docker_data/mod.rs | 105 ++++++++++++++------------------ src/exec.rs | 17 +++--- src/ui/draw_blocks.rs | 8 ++- src/ui/mod.rs | 2 +- 6 files changed, 93 insertions(+), 92 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 55ea1d6..6cc73f7 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -110,10 +110,10 @@ pub struct ContainerPorts { pub public: Option, } -impl From<&Port> for ContainerPorts { - fn from(value: &Port) -> Self { +impl From for ContainerPorts { + fn from(value: Port) -> Self { Self { - ip: value.ip.clone(), + ip: value.ip, private: value.private_port, public: value.public_port, } @@ -258,9 +258,12 @@ pub enum State { } impl State { + /// The container is alive if the start is Running, either healthy or unhealthy pub const fn is_alive(self) -> bool { matches!(self, Self::Running(_)) } + /// Color of the state for the containers section + /// TODO allow usable editable colours pub const fn get_color(self) -> Color { match self { Self::Paused => Color::Yellow, diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index f0f05f1..8cbaf01 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -251,7 +251,7 @@ impl AppData { self.filter_containers(); } - // change the filter_by option + /// change the filter_by option pub fn filter_by_next(&mut self) { if let Some(by) = self.filter.by.next() { self.filter.by = by; @@ -259,7 +259,7 @@ impl AppData { } } - // change the filter_by option + /// change the filter_by option pub fn filter_by_prev(&mut self) { if let Some(by) = self.filter.by.prev() { self.filter.by = by; @@ -393,6 +393,14 @@ impl AppData { self.containers.items.len() } + pub fn get_all_id_state(&self) -> Vec<(State, ContainerId)> { + self.containers + .items + .iter() + .map(|i| (i.state, i.id.clone())) + .collect::>() + } + /// Get all the ContainerItems pub fn get_container_items(&self) -> &[ContainerItem] { &self.containers.items @@ -478,11 +486,10 @@ impl AppData { } /// Get Option of the current selected container's ports, sorted by private port - pub fn get_selected_ports(&mut self) -> Option<(Vec, State)> { + pub fn get_selected_ports(&mut self) -> Option<(&[ContainerPorts], State)> { if let Some(item) = self.get_mut_selected_container() { - let mut ports = item.ports.clone(); - ports.sort_by(|a, b| a.private.cmp(&b.private)); - return Some((ports, item.state)); + item.ports.sort_by(|a, b| a.private.cmp(&b.private)); + return Some((&item.ports, item.state)); } None } @@ -506,12 +513,12 @@ impl AppData { } /// Get the ContainerName of by ID - pub fn get_container_name_by_id(&mut self, id: &ContainerId) -> Option { + pub fn get_container_name_by_id(&mut self, id: &ContainerId) -> Option<&ContainerName> { self.containers .items .iter_mut() .find(|i| &i.id == id) - .map(|i| i.name.clone()) + .map(|i| &i.name) } /// Find the id of the currently selected container. @@ -692,7 +699,6 @@ impl AppData { let mut columns = Columns::new(); let count = |x: &str| u8::try_from(x.chars().count()).unwrap_or(12); - // Should probably find a refactor here somewhere for container in [&self.containers.items, &self.hidden_containers] { for container in container { let cpu_count = container.cpu_stats.back().map_or_else( @@ -759,12 +765,11 @@ impl AppData { container.tx.update(tx); container.mem_limit.update(mem_limit); } - // need to benchmark this? self.sort_containers(); } /// Update, or insert, containers - pub fn update_containers(&mut self, all_containers: &mut [ContainerSummary]) { + pub fn update_containers(&mut self, mut all_containers: Vec) { let all_ids = self .containers .items @@ -799,7 +804,7 @@ impl AppData { } } - for i in all_containers { + for mut i in all_containers { if let Some(id) = i.id.as_ref() { let name = i.names.as_mut().map_or(String::new(), |names| { names.first_mut().map_or(String::new(), |f| { @@ -810,8 +815,8 @@ impl AppData { }) }); - let ports = i.ports.as_ref().map_or(vec![], |i| { - i.iter().map(ContainerPorts::from).collect::>() + let ports = i.ports.map_or(vec![], |i| { + i.into_iter().map(ContainerPorts::from).collect::>() }); let id = ContainerId::from(id.as_str()); @@ -1466,7 +1471,7 @@ mod tests { let mut app_data = gen_appdata(&containers); let result = app_data.get_container_name_by_id(&ContainerId::from("2")); - assert_eq!(result, Some(ContainerName::from("container_2"))); + assert_eq!(result, Some(&ContainerName::from("container_2"))); } #[test] @@ -2173,7 +2178,8 @@ mod tests { private: 8001, public: None } - ], + ] + .as_slice(), State::Running(RunningState::Healthy), )) ); @@ -2185,7 +2191,7 @@ mod tests { assert_eq!( result, - Some((vec![], State::Running(RunningState::Healthy))) + Some((vec![].as_slice(), State::Running(RunningState::Healthy))) ); } @@ -2220,12 +2226,12 @@ mod tests { let (_ids, containers) = gen_containers(); let mut app_data = gen_appdata(&containers); let result_pre = app_data.get_container_items().to_owned(); - let mut input = [ + let input = vec![ gen_container_summary(1, "paused"), gen_container_summary(2, "dead"), ]; - app_data.update_containers(&mut input); + app_data.update_containers(input); let result_post = app_data.get_container_items().to_owned(); assert_ne!(result_pre, result_post); assert_eq!(result_post[0].state, State::Paused); diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 9a42b99..4841e7d 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -19,7 +19,7 @@ use tokio::{ use uuid::Uuid; use crate::{ - app_data::{AppData, ContainerId, ContainerStatus, DockerCommand, State}, + app_data::{AppData, ContainerId, DockerCommand, State}, app_error::AppError, parse_args::CliArgs, ui::{GuiState, Status}, @@ -34,6 +34,15 @@ enum SpawnId { Log(ContainerId), } +impl SpawnId { + /// Extract the &ContainerId out of self + const fn get_id(&self) -> &ContainerId { + match self { + Self::Log(id) | Self::Stats((id, _)) => id, + } + } +} + /// Cpu & Mem stats take twice as long as the update interval to get a value, so will have two being executed at the same time /// SpawnId::Stats takes container_id and binate value to enable both cycles of the same container_id to be inserted into the hashmap /// Binate value is toggled when all handles have been spawned off @@ -104,12 +113,12 @@ impl DockerData { async fn update_container_stat( app_data: Arc>, docker: Arc, - id: ContainerId, state: State, spawn_id: SpawnId, spawns: Arc>>>, ) { if state.is_alive() { + let id = spawn_id.get_id(); let mut stream = docker .stats( id.get(), @@ -162,31 +171,27 @@ impl DockerData { app_data .lock() - .update_stats_by_id(&id, cpu_stats, mem_stat, mem_limit, rx, tx); + .update_stats_by_id(id, cpu_stats, mem_stat, mem_limit, rx, tx); } } spawns.lock().remove(&spawn_id); } /// Update all stats, spawn each container into own tokio::spawn thread - fn update_all_container_stats(&mut self, all_ids: &[(State, ContainerId)]) { + fn update_all_container_stats(&mut self) { + let all_ids = self.app_data.lock().get_all_id_state(); for (state, id) in all_ids { - let docker = Arc::clone(&self.docker); - let app_data = Arc::clone(&self.app_data); - let spawns = Arc::clone(&self.spawns); - let spawn_id = SpawnId::Stats((id.clone(), self.binate)); - + let spawn_id = SpawnId::Stats((id, self.binate)); self.spawns .lock() .entry(spawn_id.clone()) .or_insert_with(|| { tokio::spawn(Self::update_container_stat( - app_data, - docker, - id.clone(), - *state, + Arc::clone(&self.app_data), + Arc::clone(&self.docker), + state, spawn_id, - spawns, + Arc::clone(&self.spawns), )) }); } @@ -196,7 +201,7 @@ impl DockerData { /// Get all current containers, handle into ContainerItem in the app_data struct rather than here /// Just make sure that items sent are guaranteed to have an id /// If in a containerised runtime, will ignore any container that uses the `/app/oxker` as an entry point, unless the `-s` flag is set - async fn update_all_containers(&self) -> Vec<(State, ContainerId)> { + async fn update_all_containers(&self) { let containers = self .docker .list_containers(Some(ListContainersOptions:: { @@ -206,7 +211,7 @@ impl DockerData { .await .unwrap_or_default(); - let mut output = containers + let output = containers .into_iter() .filter_map(|f| match f.id { Some(_) => { @@ -225,23 +230,7 @@ impl DockerData { }) .collect::>(); - self.app_data.lock().update_containers(&mut output); - - // Just get the containers that are currently running, or being restarted, no point updating info on paused or dead containers - output - .into_iter() - .filter_map(|i| { - i.id.map(|id| { - ( - State::from(( - i.state, - &ContainerStatus::from(i.status.map_or_else(String::new, |i| i)), - )), - ContainerId::from(id.as_str()), - ) - }) - }) - .collect::>() + self.app_data.lock().update_containers(output); } /// Update single container logs @@ -250,7 +239,6 @@ impl DockerData { app_data: Arc>, docker: Arc, id: ContainerId, - init: Option>, since: u64, spawns: Arc>>>, stderr: bool, @@ -272,28 +260,29 @@ impl DockerData { output.push(data); } } - spawns.lock().remove(&SpawnId::Log(id.clone())); app_data.lock().update_log_by_id(output, &id); - init.map(|i| i.fetch_add(1, std::sync::atomic::Ordering::SeqCst)); + spawns.lock().remove(&SpawnId::Log(id)); } /// Update all logs, spawn each container into own tokio::spawn thread - fn init_all_logs(&self, all_ids: &[(State, ContainerId)], init: Option<&Arc>) { + fn init_all_logs(&self, all_ids: Vec<(State, ContainerId)>) -> Arc { + let init = Arc::new(AtomicUsize::new(0)); for (_, id) in all_ids { - // let init = init.map(|i|Arc::clone(i)); + let app_data: Arc> = + Arc::clone(&self.app_data); + let docker = Arc::clone(&self.docker); + let spawns = Arc::clone(&self.spawns); + let std_err = self.args.std_err; + let init = Arc::clone(&init); self.spawns.lock().insert( SpawnId::Log(id.clone()), - tokio::spawn(Self::update_log( - Arc::clone(&self.app_data), - Arc::clone(&self.docker), - id.clone(), - init.map(Arc::clone), - 0, - Arc::clone(&self.spawns), - self.args.std_err, - )), + tokio::spawn(async move { + Self::update_log(app_data, docker, id, 0, spawns, std_err).await; + init.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + }), ); } + init } /// Initialize docker container data, before any messages are received @@ -301,14 +290,13 @@ impl DockerData { self.gui_state.lock().status_push(Status::Init); let loading_uuid = Uuid::new_v4(); GuiState::start_loading_animation(&self.gui_state, loading_uuid); - let all_ids = self.update_all_containers().await; + self.update_all_containers().await; + let all_ids = self.app_data.lock().get_all_id_state(); + let all_ids_len = all_ids.len(); + let init = self.init_all_logs(all_ids); + self.update_all_container_stats(); - self.update_all_container_stats(&all_ids); - - let init = Arc::new(AtomicUsize::new(0)); - self.init_all_logs(&all_ids, Some(&init)); - - while init.load(std::sync::atomic::Ordering::SeqCst) != all_ids.len() { + while init.load(std::sync::atomic::Ordering::SeqCst) != all_ids_len { self.app_data.lock().sort_containers(); tokio::time::sleep(std::time::Duration::from_millis(10)).await; } @@ -318,7 +306,7 @@ impl DockerData { /// Update all cpu_mem, and selected container log (if a log update join_handle isn't currently being executed) async fn update_everything(&mut self) { - let all_ids = self.update_all_containers().await; + self.update_all_containers().await; if let Some(container) = self.app_data.lock().get_selected_container() { let last_updated = container.last_updated; self.spawns @@ -329,14 +317,13 @@ impl DockerData { Arc::clone(&self.app_data), Arc::clone(&self.docker), container.id.clone(), - None, last_updated, Arc::clone(&self.spawns), self.args.std_err, )) }); }; - self.update_all_container_stats(&all_ids); + self.update_all_container_stats(); self.app_data.lock().sort_containers(); } @@ -438,7 +425,7 @@ impl DockerData { if app_data.lock().get_error().is_none() { let mut inner = Self { app_data, - args: args.clone(), + args, binate: Binate::One, docker: Arc::new(docker), gui_state, @@ -446,7 +433,7 @@ impl DockerData { spawns: Arc::new(Mutex::new(HashMap::new())), }; inner.initialise_container_data().await; - Self::heartbeat(&args, docker_tx); + Self::heartbeat(&inner.args, docker_tx); inner.message_handler().await; } } diff --git a/src/exec.rs b/src/exec.rs index 283a945..ba77737 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -144,9 +144,9 @@ impl TerminalSize { #[derive(Debug, Clone)] pub enum ExecMode { // use Bollard Rust library - Internal((ContainerId, Arc)), + Internal((Arc, Arc)), // use the external `docker-cli` - External(ContainerId), + External(Arc), } impl ExecMode { @@ -186,7 +186,10 @@ impl ExecMode { { if let Some(Ok(msg)) = output.next().await { if !msg.to_string().starts_with(OCI_ERROR) { - return Some(Self::Internal((id.clone(), Arc::clone(docker)))); + return Some(Self::Internal(( + Arc::new(id), + Arc::clone(docker), + ))); } } } @@ -199,7 +202,7 @@ impl ExecMode { { if let Ok(output) = String::from_utf8(output.stdout) { if !output.starts_with(OCI_ERROR) { - return Some(Self::External(id.clone())); + return Some(Self::External(Arc::new(id))); } } } @@ -302,9 +305,9 @@ impl ExecMode { Ok(()) } - // This is the fix for key pressed not being handled correctly on quit - // It writes a special message to the stdout, and then listens out for a valid response - // afterwhich it's assumes that we're completely done with TTY + /// This is the fix for key pressed not being handled correctly on quit + /// It writes a special message to the stdout, and then listens out for a valid response + /// afterwhich it's assumes that we're completely done with TTY fn internal_cleanup(&self) -> Result<(), AppError> { match self { Self::External(_) => Ok(()), diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 395bf31..0f2f840 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -294,7 +294,8 @@ pub fn ports( app_data: &Arc>, max_lens: (usize, usize, usize), ) { - let ports = app_data.lock().get_selected_ports(); + let mut data = app_data.lock(); + let ports = data.get_selected_ports(); if let Some(ports) = ports { let block = Block::default() .borders(Borders::ALL) @@ -318,6 +319,7 @@ pub fn ports( .alignment(Alignment::Center) .block(block); f.render_widget(paragraph, area); + drop(data); } else { let mut output = vec![Line::from( Span::from(format!( @@ -326,7 +328,7 @@ pub fn ports( )) .fg(Color::Yellow), )]; - for item in &ports.0 { + for item in ports.0 { let fg = Color::White; let strings = item.print(); @@ -1244,7 +1246,7 @@ mod tests { setup .app_data .lock() - .update_containers(&mut vec![gen_container_summary(1, "paused")]); + .update_containers(vec![gen_container_summary(1, "paused")]); setup.app_data.lock().docker_controls_next(); let expected = [ diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 9c209e0..0dfd492 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -326,7 +326,7 @@ fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc Date: Tue, 3 Dec 2024 20:39:51 +0000 Subject: [PATCH 25/44] feat: ContainerPorts use ipaddr --- src/app_data/container_state.rs | 15 +++++++++------ src/app_data/mod.rs | 21 +++++++++------------ src/docker_data/mod.rs | 2 +- src/ui/draw_blocks.rs | 16 ++++++++++------ 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 6cc73f7..d48e279 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -2,6 +2,7 @@ use std::{ cmp::Ordering, collections::{HashSet, VecDeque}, fmt, + net::IpAddr, }; use bollard::service::Port; @@ -103,9 +104,9 @@ macro_rules! unit_struct { unit_struct!(ContainerName); unit_struct!(ContainerImage); -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ContainerPorts { - pub ip: Option, + pub ip: Option, pub private: u16, pub public: Option, } @@ -113,7 +114,7 @@ pub struct ContainerPorts { impl From for ContainerPorts { fn from(value: Port) -> Self { Self { - ip: value.ip, + ip: value.ip.and_then(|i| i.parse::().ok()), private: value.private_port, public: value.public_port, } @@ -122,7 +123,9 @@ impl From for ContainerPorts { impl ContainerPorts { pub fn len_ip(&self) -> usize { - self.ip.as_ref().unwrap_or(&String::new()).chars().count() + self.ip + .as_ref() + .map_or(0, |i|i.to_string().chars().count()) } pub fn len_private(&self) -> usize { format!("{}", self.private).chars().count() @@ -134,11 +137,11 @@ impl ContainerPorts { } /// Return as tuple of Strings, ip address, private port, and public port - pub fn print(&self) -> (String, String, String) { + pub fn get_all(&self) -> (String, String, String) { ( self.ip .as_ref() - .map_or(String::new(), std::borrow::ToOwned::to_owned), + .map_or(String::new(), std::string::ToString::to_string), format!("{}", self.private), self.public.map_or(String::new(), |s| s.to_string()), ) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 8cbaf01..ae7dabb 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -450,39 +450,36 @@ impl AppData { } /// Find the longest port when it's transformed into a string, defaults are header lens (ip, private, public) - /// TODO refactor this, and write comments as to whete the initial sizes come from + ///display like this: "│ ip, private, public│", so (5,10,9) are the minimum lengths required pub fn get_longest_port(&self) -> (usize, usize, usize) { - let mut longest_ip = 5; - let mut longest_private = 10; - let mut longest_public = 9; + let mut output = (5, 10, 9); for item in [&self.containers.items, &self.hidden_containers] { for item in item { - longest_ip = longest_ip.max( + output.0 = output.0.max( item.ports .iter() .map(ContainerPorts::len_ip) .max() - .unwrap_or(3), + .unwrap_or(output.0), ); - longest_private = longest_private.max( + output.1 = output.1.max( item.ports .iter() .map(ContainerPorts::len_private) .max() - .unwrap_or(8), + .unwrap_or(output.1), ); - longest_public = longest_public.max( + output.2 = output.2.max( item.ports .iter() .map(ContainerPorts::len_public) .max() - .unwrap_or(6), + .unwrap_or(output.2), ); } } - - (longest_ip, longest_private, longest_public) + output } /// Get Option of the current selected container's ports, sorted by private port diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 4841e7d..03c6e51 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -292,7 +292,7 @@ impl DockerData { GuiState::start_loading_animation(&self.gui_state, loading_uuid); self.update_all_containers().await; let all_ids = self.app_data.lock().get_all_id_state(); - let all_ids_len = all_ids.len(); + let all_ids_len = all_ids.len(); let init = self.init_all_logs(all_ids); self.update_all_container_stats(); diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 0f2f840..b78a06c 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -330,7 +330,7 @@ pub fn ports( )]; for item in ports.0 { let fg = Color::White; - let strings = item.print(); + let strings = item.get_all(); let line = vec![ Span::from(format!("{:>ip$}", strings.0)).fg(fg), @@ -1066,7 +1066,11 @@ fn popup(text_lines: usize, text_width: usize, r: Rect, box_location: BoxLocatio #[allow(clippy::unwrap_used)] mod tests { - use std::{ops::RangeInclusive, sync::Arc}; + use std::{ + net::{IpAddr, Ipv4Addr}, + ops::RangeInclusive, + sync::Arc, + }; use parking_lot::Mutex; use ratatui::{ @@ -3171,7 +3175,7 @@ mod tests { setup.app_data.lock().containers.items[0] .ports .push(ContainerPorts { - ip: Some("127.0.0.1".to_owned()), + ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), private: 8003, public: Some(8003), }); @@ -3317,7 +3321,7 @@ mod tests { setup.app_data.lock().containers.items[0] .ports .push(ContainerPorts { - ip: Some("127.0.0.1".to_owned()), + ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), private: 8003, public: Some(8003), }); @@ -3381,7 +3385,7 @@ mod tests { setup.app_data.lock().containers.items[1] .ports .push(ContainerPorts { - ip: Some("127.0.0.1".to_owned()), + ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), private: 8003, public: Some(8003), }); @@ -3498,7 +3502,7 @@ mod tests { setup.app_data.lock().containers.items[0] .ports .push(ContainerPorts { - ip: Some("127.0.0.1".to_owned()), + ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), private: 8003, public: Some(8003), }); From 1a8dab654a1fdbf351a72dc54fe3d1943355bba6 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:42:24 +0000 Subject: [PATCH 26/44] refactor: remove app_data param from generate_lock() insert data into FrameData instead --- src/app_data/container_state.rs | 2 +- src/app_data/mod.rs | 2 +- src/ui/draw_blocks.rs | 11 +++++------ src/ui/mod.rs | 4 ++++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index d48e279..69b5c5e 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -125,7 +125,7 @@ impl ContainerPorts { pub fn len_ip(&self) -> usize { self.ip .as_ref() - .map_or(0, |i|i.to_string().chars().count()) + .map_or(0, |i| i.to_string().chars().count()) } pub fn len_private(&self) -> usize { format!("{}", self.private).chars().count() diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index ae7dabb..9cbd0c8 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -407,7 +407,7 @@ impl AppData { } /// Get title for containers section, add a suffix indicating if the containers are currently under filter - pub fn container_title(&self) -> String { + pub fn get_container_title(&self) -> String { let suffix = if !self.hidden_containers.is_empty() && !self.containers.items.is_empty() { " - filtered" } else { diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index b78a06c..6731b5e 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -72,7 +72,6 @@ fn max_line_width(text: &str) -> usize { /// Generate block, add a border if is the selected panel, /// add custom title based on state of each panel fn generate_block<'a>( - app_data: &Arc>, area: Rect, fd: &FrameData, gui_state: &Arc>, @@ -83,10 +82,10 @@ fn generate_block<'a>( .update_region_map(Region::Panel(panel), area); let mut title = match panel { SelectablePanel::Containers => { - format!("{}{}", panel.title(), app_data.lock().container_title()) + format!("{}{}", panel.title(), fd.container_title) } SelectablePanel::Logs => { - format!("{}{}", panel.title(), app_data.lock().get_log_title()) + format!("{}{}", panel.title(), fd.log_title) } SelectablePanel::Commands => String::new(), }; @@ -111,7 +110,7 @@ pub fn commands( fd: &FrameData, gui_state: &Arc>, ) { - let block = generate_block(app_data, area, fd, gui_state, SelectablePanel::Commands); + let block = generate_block(area, fd, gui_state, SelectablePanel::Commands); let items = app_data.lock().get_control_items().map_or(vec![], |i| { i.iter() .map(|c| { @@ -220,7 +219,7 @@ pub fn containers( fd: &FrameData, gui_state: &Arc>, ) { - let block = generate_block(app_data, area, fd, gui_state, SelectablePanel::Containers); + let block = generate_block(area, fd, gui_state, SelectablePanel::Containers); let items = app_data .lock() @@ -259,7 +258,7 @@ pub fn logs( fd: &FrameData, gui_state: &Arc>, ) { - let block = generate_block(app_data, area, fd, gui_state, SelectablePanel::Logs); + let block = generate_block(area, fd, gui_state, SelectablePanel::Logs); if fd.init { let paragraph = Paragraph::new(format!("parsing logs {}", fd.loading_icon)) .style(Style::default()) diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 0dfd492..3a41c1d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -224,6 +224,8 @@ impl Ui { #[derive(Debug)] pub struct FrameData { columns: Columns, + container_title: String, + log_title: String, delete_confirm: Option, has_containers: bool, has_error: Option, @@ -248,6 +250,8 @@ impl From<(MutexGuard<'_, AppData>, MutexGuard<'_, GuiState>)> for FrameData { Self { columns: data.0.get_width(), + container_title: data.0.get_container_title(), + log_title: data.0.get_log_title(), delete_confirm: data.1.get_delete_container(), has_containers: data.0.get_container_len() > 0, has_error: data.0.get_error(), From 9b22f5da18e4bf92766a68a7f4cd61ad72724cfd Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 3 Dec 2024 21:29:49 +0000 Subject: [PATCH 27/44] tests: fix logs tests --- src/ui/draw_blocks.rs | 52 ++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 6731b5e..65e8702 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -1879,21 +1879,18 @@ mod tests { #[test] /// No logs, panel unselected, then selected, border color changes correctly fn test_draw_blocks_logs_none() { - let (w, h) = (25, 6); + let (w, h) = (35, 6); let mut setup = test_setup(w, h, true, true); - setup.app_data.lock().containers = StatefulList::new(vec![]); let expected = [ - "╭ Logs ─────────────────╮", - "│ no logs found │", - "│ │", - "│ │", - "│ │", - "╰───────────────────────╯", + "╭ Logs - container_1 - image_1 ───╮", + "│ no logs found │", + "│ │", + "│ │", + "│ │", + "╰─────────────────────────────────╯", ]; - let _fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); - setup .terminal .draw(|f| { @@ -1925,7 +1922,6 @@ mod tests { let expected_row = expected_to_vec(&expected, row_index); for (result_cell_index, result_cell) in result_row.iter().enumerate() { assert_eq!(result_cell.symbol(), expected_row[result_cell_index]); - if BORDER_CHARS.contains(&result_cell.symbol()) { assert_eq!(result_cell.fg, Color::LightCyan); } @@ -2001,7 +1997,7 @@ mod tests { #[test] /// Logs correct displayed, changing log state also draws correctly fn test_draw_blocks_logs_some() { - let (w, h) = (25, 6); + let (w, h) = (36, 6); let mut setup = test_setup(w, h, true, true); insert_logs(&setup); @@ -2014,12 +2010,12 @@ mod tests { }) .unwrap(); let expected = [ - "╭ Logs 3/3 - container_1╮", - "│ line 1 │", - "│ line 2 │", - "│▶ line 3 │", - "│ │", - "╰───────────────────────╯", + "╭ Logs 3/3 - container_1 - image_1 ╮", + "│ line 1 │", + "│ line 2 │", + "│▶ line 3 │", + "│ │", + "╰──────────────────────────────────╯", ]; for (row_index, result_row) in get_result(&setup, w) { @@ -2028,7 +2024,7 @@ mod tests { assert_eq!(result_cell.symbol(), expected_row[result_cell_index]); assert_eq!(result_cell.fg, Color::Reset); - if row_index == 3 && (1..=23).contains(&result_cell_index) { + if row_index == 3 && (1..=34).contains(&result_cell_index) { assert_eq!(result_cell.modifier, Modifier::BOLD); } else { assert!(result_cell.modifier.is_empty()); @@ -2038,22 +2034,22 @@ mod tests { // Change selected log line setup.app_data.lock().log_previous(); - _ = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); setup .terminal .draw(|f| { - super::logs(&setup.app_data, setup.area, f, &setup.fd, &setup.gui_state); + super::logs(&setup.app_data, setup.area, f, &fd, &setup.gui_state); }) .unwrap(); let expected = [ - "╭ Logs 2/3 - container_1╮", - "│ line 1 │", - "│▶ line 2 │", - "│ line 3 │", - "│ │", - "╰───────────────────────╯", + "╭ Logs 2/3 - container_1 - image_1 ╮", + "│ line 1 │", + "│▶ line 2 │", + "│ line 3 │", + "│ │", + "╰──────────────────────────────────╯", ]; for (row_index, result_row) in get_result(&setup, w) { let expected_row = expected_to_vec(&expected, row_index); @@ -2061,7 +2057,7 @@ mod tests { assert_eq!(result_cell.symbol(), expected_row[result_cell_index]); assert_eq!(result_cell.fg, Color::Reset); - if row_index == 2 && (1..=23).contains(&result_cell_index) { + if row_index == 2 && (1..=34).contains(&result_cell_index) { assert_eq!(result_cell.modifier, Modifier::BOLD); } else { assert!(result_cell.modifier.is_empty()); From 356ea5549bb4877e9893fe0e1053e73c5a62e806 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 3 Dec 2024 21:59:00 +0000 Subject: [PATCH 28/44] refactor: get_filter combine filter_term and term_by into a tuple, and insert into FrameData, to reduce .lock() calls --- src/app_data/mod.rs | 53 +++++++++++++++++++++++-------------------- src/ui/draw_blocks.rs | 26 ++++++++++----------- src/ui/mod.rs | 15 ++++++++---- 3 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 9cbd0c8..5e4ed06 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -160,14 +160,9 @@ impl AppData { } /// Filter related methods - /// Get the current filter term - pub const fn get_filter_term(&self) -> Option<&String> { - self.filter.term.as_ref() - } - - /// Get the current filter by choice - pub const fn get_filter_by(&self) -> FilterBy { - self.filter.by + /// Get the filterby and filter_term + pub const fn get_filter(&self) -> (FilterBy, Option<&String>) { + (self.filter.by, self.filter.term.as_ref()) } /// Check if a given container can be inserted into the "visible" list, based on current filter term and filter_by @@ -1646,13 +1641,13 @@ mod tests { let mut app_data = gen_appdata(&containers); - assert!(app_data.get_filter_term().is_none()); + assert!(app_data.get_filter().1.is_none()); let pre_len = app_data.containers.items.len(); app_data.filter_term_push('_'); app_data.filter_term_push('2'); - assert_eq!(app_data.get_filter_term(), Some(&"_2".to_string())); + assert_eq!(app_data.get_filter().1, Some(&"_2".to_string())); app_data.filter_containers(); let post_len = app_data.containers.items.len(); @@ -1672,7 +1667,7 @@ mod tests { let mut app_data = gen_appdata(&containers); - assert!(app_data.get_filter_term().is_none()); + assert!(app_data.get_filter().1.is_none()); let pre_len = app_data.containers.items.len(); for c in ['i', 'm', 'a', 'g', 'e', '_', '2'] { @@ -1681,8 +1676,10 @@ mod tests { // app_data.filter_term_push('2'); app_data.filter_by_next(); - assert_eq!(app_data.get_filter_by(), FilterBy::Image); - assert_eq!(app_data.get_filter_term(), Some(&"image_2".to_string())); + assert_eq!( + app_data.get_filter(), + (FilterBy::Image, Some(&"image_2".to_string())) + ); app_data.filter_containers(); let post_len = app_data.containers.items.len(); @@ -1701,7 +1698,7 @@ mod tests { ContainerStatus::from("Exited".to_owned()).clone_into(&mut containers[0].status); let mut app_data = gen_appdata(&containers); - assert!(app_data.get_filter_term().is_none()); + assert!(app_data.get_filter().1.is_none()); let pre_len = app_data.containers.items.len(); app_data.filter_term_push('x'); @@ -1709,8 +1706,10 @@ mod tests { app_data.filter_by_next(); app_data.filter_by_next(); - assert_eq!(app_data.get_filter_by(), FilterBy::Status); - assert_eq!(app_data.get_filter_term(), Some(&"x".to_string())); + assert_eq!( + app_data.get_filter(), + (FilterBy::Status, Some(&"x".to_string())) + ); app_data.filter_containers(); let post_len = app_data.containers.items.len(); @@ -1729,7 +1728,7 @@ mod tests { ContainerStatus::from("Exited".to_owned()).clone_into(&mut containers[0].status); let mut app_data = gen_appdata(&containers); - assert!(app_data.get_filter_term().is_none()); + assert!(app_data.get_filter().1.is_none()); let pre_len = app_data.containers.items.len(); app_data.filter_term_push('x'); @@ -1738,8 +1737,10 @@ mod tests { app_data.filter_by_next(); app_data.filter_by_next(); - assert_eq!(app_data.get_filter_by(), FilterBy::All); - assert_eq!(app_data.get_filter_term(), Some(&"x".to_string())); + assert_eq!( + app_data.get_filter(), + (FilterBy::All, Some(&"image_2".to_string())) + ); app_data.filter_containers(); let post_len = app_data.containers.items.len(); @@ -1758,7 +1759,7 @@ mod tests { ContainerStatus::from("Exited".to_owned()).clone_into(&mut containers[0].status); let mut app_data = gen_appdata(&containers); - assert!(app_data.get_filter_term().is_none()); + assert!(app_data.get_filter().1.is_none()); let pre_len = app_data.containers.items.len(); app_data.filter_term_push('x'); @@ -1766,8 +1767,10 @@ mod tests { app_data.filter_by_next(); app_data.filter_by_next(); - assert_eq!(app_data.get_filter_by(), FilterBy::Status); - assert_eq!(app_data.get_filter_term(), Some(&"x".to_string())); + assert_eq!( + app_data.get_filter(), + (FilterBy::Status, Some(&"x".to_string())) + ); app_data.filter_containers(); let post_len = app_data.containers.items.len(); @@ -1779,8 +1782,10 @@ mod tests { assert!(!app_data.can_insert(&containers[2])); app_data.filter_by_prev(); - assert_eq!(app_data.get_filter_by(), FilterBy::Image); - assert_eq!(app_data.get_filter_term(), Some(&"x".to_string())); + assert_eq!( + app_data.get_filter(), + (FilterBy::Image, Some(&"x".to_string())) + ); app_data.filter_containers(); let post_len = app_data.containers.items.len(); diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 65e8702..8a6a646 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -229,7 +229,7 @@ pub fn containers( .collect::>(); if items.is_empty() { - let text = if app_data.lock().get_filter_term().is_some() { + let text = if fd.filter_term.is_some() { "no containers match filter" } else if gui_state.lock().is_loading() { &format!("loading {}", fd.loading_icon) @@ -423,16 +423,14 @@ fn make_chart<'a, T: Stats + Display>( } /// Create the filter_by by spans, coloured dependant on which one is selected -fn filter_by_spans(app_data: &Arc>) -> [Span; 4] { - let filter_by = app_data.lock().get_filter_by(); - +fn filter_by_spans(fd: &FrameData) -> [Span; 4] { let selected = Style::default().bg(Color::Gray).fg(Color::Black); let not_selected = Style::default().bg(Color::Reset).fg(Color::Reset); // This should be refactored somehow let name = [" Name ", " Image ", " Status ", " All "]; - match filter_by { + match fd.filter_by { FilterBy::Name => [ Span::styled(name[0], selected), Span::styled(name[1], not_selected), @@ -461,7 +459,7 @@ fn filter_by_spans(app_data: &Arc>) -> [Span; 4] { } /// Draw the filter bar -pub fn filter_bar(area: Rect, frame: &mut Frame, app_data: &Arc>) { +pub fn filter_bar(area: Rect, frame: &mut Frame, fd: &FrameData) { let style_but = Style::default().fg(Color::Black).bg(Color::Magenta); let style_desc = Style::default().fg(Color::Gray).bg(Color::Reset); @@ -471,7 +469,7 @@ pub fn filter_bar(area: Rect, frame: &mut Frame, app_data: &Arc>) Span::styled(" ← by → ", style_but), Span::from(" "), ]; - line.extend_from_slice(&filter_by_spans(app_data)); + line.extend_from_slice(&filter_by_spans(fd)); line.extend_from_slice(&[ Span::styled( " term: ", @@ -480,10 +478,9 @@ pub fn filter_bar(area: Rect, frame: &mut Frame, app_data: &Arc>) .add_modifier(Modifier::BOLD), ), Span::styled( - app_data - .lock() - .get_filter_term() - .map_or(String::new(), std::borrow::ToOwned::to_owned), + fd.filter_term + .as_ref() + .map_or(String::new(), std::clone::Clone::clone), Style::default().fg(Color::Gray), ), ]); @@ -2845,7 +2842,7 @@ mod tests { setup .terminal .draw(|f| { - super::filter_bar(setup.area, f, &setup.app_data); + super::filter_bar(setup.area, f, &setup.fd); }) .unwrap(); @@ -2890,7 +2887,7 @@ mod tests { setup .terminal .draw(|f| { - super::filter_bar(setup.area, f, &setup.app_data); + super::filter_bar(setup.area, f, &setup.fd); }) .unwrap(); @@ -2931,10 +2928,11 @@ mod tests { // Test when filter_by chances setup.app_data.lock().filter_by_next(); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); setup .terminal .draw(|f| { - super::filter_bar(setup.area, f, &setup.app_data); + super::filter_bar(setup.area, f, &fd); }) .unwrap(); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 3a41c1d..31140a1 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -26,7 +26,7 @@ mod gui_state; pub use self::color_match::*; pub use self::gui_state::{DeleteButton, GuiState, SelectablePanel, Status}; use crate::{ - app_data::{AppData, Columns, ContainerId, Header, SortedOrder}, + app_data::{AppData, Columns, ContainerId, FilterBy, Header, SortedOrder}, app_error::AppError, exec::TerminalSize, input_handler::InputMessages, @@ -228,6 +228,8 @@ pub struct FrameData { log_title: String, delete_confirm: Option, has_containers: bool, + filter_by: FilterBy, + filter_term: Option, has_error: Option, height: u16, help_visible: bool, @@ -248,18 +250,21 @@ impl From<(MutexGuard<'_, AppData>, MutexGuard<'_, GuiState>)> for FrameData { 12 }; + let (filter_by, filter_term) = data.0.get_filter(); Self { + filter_by, + filter_term: filter_term.cloned(), + height, columns: data.0.get_width(), container_title: data.0.get_container_title(), - log_title: data.0.get_log_title(), delete_confirm: data.1.get_delete_container(), has_containers: data.0.get_container_len() > 0, has_error: data.0.get_error(), - height, help_visible: data.1.status_contains(&[Status::Help]), - init: data.1.status_contains(&[Status::Init]), info_text: data.1.info_box_text.clone(), + init: data.1.status_contains(&[Status::Init]), loading_icon: data.1.get_loading().to_string(), + log_title: data.0.get_log_title(), selected_panel: data.1.get_selected_panel(), sorted_by: data.0.get_sorted(), } @@ -319,7 +324,7 @@ fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc Date: Tue, 3 Dec 2024 23:25:30 +0000 Subject: [PATCH 29/44] refactor: FrameData --- src/app_data/mod.rs | 6 +++--- src/ui/draw_blocks.rs | 22 ++++++++++++++-------- src/ui/mod.rs | 19 +++++++++++-------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 5e4ed06..aa08972 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -627,11 +627,11 @@ impl AppData { } /// Get mutable Vec of current containers logs - pub fn get_logs(&mut self) -> Vec> { + pub fn get_logs(&self) -> Vec> { self.containers .state .selected() - .and_then(|i| self.containers.items.get_mut(i)) + .and_then(|i| self.containers.items.get(i)) .map_or(vec![], |i| i.logs.to_vec()) } @@ -1739,7 +1739,7 @@ mod tests { assert_eq!( app_data.get_filter(), - (FilterBy::All, Some(&"image_2".to_string())) + (FilterBy::All, Some(&"x".to_string())) ); app_data.filter_containers(); diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 8a6a646..9a4bd2a 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -231,7 +231,7 @@ pub fn containers( if items.is_empty() { let text = if fd.filter_term.is_some() { "no containers match filter" - } else if gui_state.lock().is_loading() { + } else if fd.is_loading { &format!("loading {}", fd.loading_icon) } else { "no containers running" @@ -250,7 +250,8 @@ pub fn containers( } } -/// Draw the logs panel +// /// Draw the logs panel +// PREV WORKING pub fn logs( app_data: &Arc>, area: Rect, @@ -267,14 +268,13 @@ pub fn logs( f.render_widget(paragraph, area); } else { let logs = app_data.lock().get_logs(); - if logs.is_empty() { let paragraph = Paragraph::new("no logs found") .block(block) .alignment(Alignment::Center); f.render_widget(paragraph, area); } else { - let items = List::new(logs) + let items = List::new(app_data.lock().get_logs()) .block(block) .highlight_symbol(RIGHT_ARROW) .highlight_style(Style::default().add_modifier(Modifier::BOLD)); @@ -1004,13 +1004,13 @@ pub fn error(f: &mut Frame, error: AppError, seconds: Option) { /// Draw info box in one of the 9 BoxLocations // TODO is this broken - I don't think so -pub fn info(f: &mut Frame, text: &str, instant: Instant, gui_state: &Arc>) { +pub fn info(f: &mut Frame, text: String, instant: Instant, gui_state: &Arc>) { let block = Block::default() .title("") .title_alignment(Alignment::Center) .borders(Borders::NONE); - let mut max_line_width = max_line_width(text); + let mut max_line_width = max_line_width(&text); let mut lines = text.lines().count(); // Add some horizontal & vertical margins @@ -2804,7 +2804,12 @@ mod tests { setup .terminal .draw(|f| { - super::info(f, "test", std::time::Instant::now(), &setup.gui_state); + super::info( + f, + "test".to_owned(), + std::time::Instant::now(), + &setup.gui_state, + ); }) .unwrap(); @@ -2883,11 +2888,12 @@ mod tests { // Test when char added to search term setup.app_data.lock().filter_term_push('c'); setup.app_data.lock().filter_term_push('d'); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); setup .terminal .draw(|f| { - super::filter_bar(setup.area, f, &setup.fd); + super::filter_bar(setup.area, f, &fd); }) .unwrap(); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 31140a1..8c0149b 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -221,21 +221,23 @@ impl Ui { /// Frequent data required by multiple framde drawing functions, can reduce mutex reads by placing it all in here /// TODO add more items to this, and split up into parts -#[derive(Debug)] +#[derive(Debug, Clone)] +#[allow(clippy::struct_excessive_bools)] pub struct FrameData { columns: Columns, container_title: String, - log_title: String, delete_confirm: Option, - has_containers: bool, filter_by: FilterBy, filter_term: Option, + has_containers: bool, has_error: Option, height: u16, help_visible: bool, - init: bool, info_text: Option<(String, Instant)>, + init: bool, + is_loading: bool, loading_icon: String, + log_title: String, selected_panel: SelectablePanel, sorted_by: Option<(Header, SortedOrder)>, } @@ -252,17 +254,18 @@ impl From<(MutexGuard<'_, AppData>, MutexGuard<'_, GuiState>)> for FrameData { let (filter_by, filter_term) = data.0.get_filter(); Self { - filter_by, - filter_term: filter_term.cloned(), - height, columns: data.0.get_width(), container_title: data.0.get_container_title(), delete_confirm: data.1.get_delete_container(), + filter_by, + filter_term: filter_term.cloned(), has_containers: data.0.get_container_len() > 0, has_error: data.0.get_error(), + height, help_visible: data.1.status_contains(&[Status::Help]), info_text: data.1.info_box_text.clone(), init: data.1.status_contains(&[Status::Init]), + is_loading: data.1.is_loading(), loading_icon: data.1.get_loading().to_string(), log_title: data.0.get_log_title(), selected_panel: data.1.get_selected_panel(), @@ -358,7 +361,7 @@ fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc Date: Tue, 3 Dec 2024 23:46:38 +0000 Subject: [PATCH 30/44] refactor: FrameData::from() use &Arc> instead of MutexGuard --- src/ui/draw_blocks.rs | 54 +++++++++++++++++++++---------------------- src/ui/mod.rs | 41 ++++++++++++++++---------------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 9a4bd2a..cebfb02 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -1119,7 +1119,7 @@ mod tests { let app_data = Arc::new(Mutex::new(app_data)); let gui_state = Arc::new(Mutex::new(gui_state)); - let fd = FrameData::from((app_data.lock(), gui_state.lock())); + let fd = FrameData::from((&app_data, &gui_state)); let area = Rect::new(0, 0, w, h); TuiTestSetup { app_data, @@ -1325,7 +1325,7 @@ mod tests { // Control panel now selected, should have a blue border setup.gui_state.lock().next_panel(); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { @@ -1374,7 +1374,7 @@ mod tests { ]; setup.gui_state.lock().next_panel(); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -1392,7 +1392,7 @@ mod tests { } setup.gui_state.lock().previous_panel(); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -1461,7 +1461,7 @@ mod tests { // Change selected panel, border is now no longer blue setup.gui_state.lock().next_panel(); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { @@ -1495,7 +1495,7 @@ mod tests { "│ │", "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯" ]; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -1555,7 +1555,7 @@ mod tests { "│ │", "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", ]; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup.app_data.lock().containers.items[0].state = State::Paused; setup @@ -1625,7 +1625,7 @@ mod tests { "│ │", "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", ]; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup.app_data.lock().containers.items[0].state = State::Paused; setup @@ -1653,7 +1653,7 @@ mod tests { "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", ]; setup.app_data.lock().containers.items[0].state = State::Dead; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -1680,7 +1680,7 @@ mod tests { "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", ]; setup.app_data.lock().containers.items[0].state = State::Exited; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -1706,7 +1706,7 @@ mod tests { "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", ]; setup.app_data.lock().containers.items[0].state = State::Removing; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -1733,7 +1733,7 @@ mod tests { "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", ]; setup.app_data.lock().containers.items[0].state = State::Restarting; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -1798,7 +1798,7 @@ mod tests { "│ │", "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯" ]; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -1857,7 +1857,7 @@ mod tests { "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", ]; setup.app_data.lock().containers.items[0].state = State::Unknown; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -1905,7 +1905,7 @@ mod tests { setup.gui_state.lock().next_panel(); setup.gui_state.lock().next_panel(); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); // When selected, has a blue border setup @@ -1943,7 +1943,7 @@ mod tests { "╰──────────────────────────────╯", ]; - let mut fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let mut fd = FrameData::from((&setup.app_data, &setup.gui_state)); fd.init = true; setup @@ -1973,7 +1973,7 @@ mod tests { "╰──────────────────────────────╯", ]; - let mut fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let mut fd = FrameData::from((&setup.app_data, &setup.gui_state)); fd.init = true; setup .terminal @@ -1999,7 +1999,7 @@ mod tests { insert_logs(&setup); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { @@ -2031,7 +2031,7 @@ mod tests { // Change selected log line setup.app_data.lock().log_previous(); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -2084,7 +2084,7 @@ mod tests { "╰──────────────────────────────────────────────────────────────────────────────╯", ]; - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { @@ -2373,7 +2373,7 @@ mod tests { let mut setup = test_setup(w, h, true, true); setup.app_data.lock().containers = StatefulList::new(vec![]); - let mut fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let mut fd = FrameData::from((&setup.app_data, &setup.gui_state)); let expected = [" ( h ) show help "]; @@ -2417,7 +2417,7 @@ mod tests { fn test_draw_blocks_headers_some_containers() { let (w, h) = (140, 1); let mut setup = test_setup(w, h, true, true); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); let expected = [" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "]; setup @@ -2448,7 +2448,7 @@ mod tests { fn test_draw_blocks_headers_some_containers_reduced_width() { let (w, h) = (80, 1); let mut setup = test_setup(w, h, true, true); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); let expected = [" name state status cpu ( h ) show help "]; @@ -2480,7 +2480,7 @@ mod tests { fn test_draw_blocks_headers_sort_containers() { let (w, h) = (140, 1); let mut setup = test_setup(w, h, true, true); - let mut fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let mut fd = FrameData::from((&setup.app_data, &setup.gui_state)); // Actual test, used for each header and sorted type let mut test = @@ -2549,7 +2549,7 @@ mod tests { let mut setup = test_setup(w, h, true, true); let uuid = Uuid::new_v4(); setup.gui_state.lock().next_loading(uuid); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); let expected = [" ⠙ name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "]; @@ -2888,7 +2888,7 @@ mod tests { // Test when char added to search term setup.app_data.lock().filter_term_push('c'); setup.app_data.lock().filter_term_push('d'); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal @@ -2934,7 +2934,7 @@ mod tests { // Test when filter_by chances setup.app_data.lock().filter_by_next(); - let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8c0149b..49a3e96 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -4,7 +4,7 @@ use crossterm::{ execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; -use parking_lot::{Mutex, MutexGuard}; +use parking_lot::Mutex; use ratatui::{ backend::CrosstermBackend, layout::{Constraint, Direction, Layout, Position}, @@ -220,7 +220,6 @@ impl Ui { } /// Frequent data required by multiple framde drawing functions, can reduce mutex reads by placing it all in here -/// TODO add more items to this, and split up into parts #[derive(Debug, Clone)] #[allow(clippy::struct_excessive_bools)] pub struct FrameData { @@ -242,41 +241,43 @@ pub struct FrameData { sorted_by: Option<(Header, SortedOrder)>, } -impl From<(MutexGuard<'_, AppData>, MutexGuard<'_, GuiState>)> for FrameData { - fn from(data: (MutexGuard<'_, AppData>, MutexGuard<'_, GuiState>)) -> Self { +impl From<(&Arc>, &Arc>)> for FrameData { + fn from(data: (&Arc>, &Arc>)) -> Self { + let (app_data, gui_data) = (data.0.lock(), data.1.lock()); + // set max height for container section, needs +5 to deal with docker commands list and borders - let height = data.0.get_container_len(); + let height = app_data.get_container_len(); let height = if height < 12 { u16::try_from(height + 5).unwrap_or_default() } else { 12 }; - let (filter_by, filter_term) = data.0.get_filter(); + let (filter_by, filter_term) = app_data.get_filter(); Self { - columns: data.0.get_width(), - container_title: data.0.get_container_title(), - delete_confirm: data.1.get_delete_container(), + columns: app_data.get_width(), + container_title: app_data.get_container_title(), + delete_confirm: gui_data.get_delete_container(), filter_by, filter_term: filter_term.cloned(), - has_containers: data.0.get_container_len() > 0, - has_error: data.0.get_error(), + has_containers: app_data.get_container_len() > 0, + has_error: app_data.get_error(), height, - help_visible: data.1.status_contains(&[Status::Help]), - info_text: data.1.info_box_text.clone(), - init: data.1.status_contains(&[Status::Init]), - is_loading: data.1.is_loading(), - loading_icon: data.1.get_loading().to_string(), - log_title: data.0.get_log_title(), - selected_panel: data.1.get_selected_panel(), - sorted_by: data.0.get_sorted(), + help_visible: gui_data.status_contains(&[Status::Help]), + info_text: gui_data.info_box_text.clone(), + init: gui_data.status_contains(&[Status::Init]), + is_loading: gui_data.is_loading(), + loading_icon: gui_data.get_loading().to_string(), + log_title: app_data.get_log_title(), + selected_panel: gui_data.get_selected_panel(), + sorted_by: app_data.get_sorted(), } } } /// Draw the main ui to a frame of the terminal fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc>) { - let fd = FrameData::from((app_data.lock(), gui_state.lock())); + let fd = FrameData::from((app_data, gui_state)); let contains_filter = gui_state.lock().status_contains(&[Status::Filter]); let whole_constraints = if contains_filter { From b596916b9773a0814fb5acae2745545e4ba13b7c Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Wed, 4 Dec 2024 09:23:47 +0000 Subject: [PATCH 31/44] docs: changelog --- CHANGELOG.md | 9 ++++++++- src/ui/mod.rs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb7eb23..049f758 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,14 @@ ### Features + `--no-stderr` cli arg, closes #52, [c739637b91c8fa742a69f4d888678d7b3964678c] ++ ContainerPorts use ipaddr, [1b26997d25f748e0d452f41fe41791533046ecdf] ### Fixes + update containerised Dockerfile, [0c6f53228f01196e352c2069383ba1e7a10950a8] + calculate_usage overflow, [5106a01f3dcb87ce5a8f1fb7bf49dc6b3c25d03e] ### Refactors -+ massively speed up docker init process, [8b9fe4246865441704ae12dff0938868a4fe6f81] ++ speed up docker logs init process, [8b9fe4246865441704ae12dff0938868a4fe6f81] + remove docker sleep, [f1562d1084336fe5be39894c93cb49107f0a4a6d] + dead code removed, [5ee48d5708fa6de0206c021db0bb611196e66fba], [ba6a95241389f99d504ee4bf3e87e19006f12e49], [f0b1145651625ad4e577d79baaf902d4d3bc0579] + input_handler, [7f4238349525c01ae9fb8b1f6c0946e5364dd55e] @@ -21,7 +22,13 @@ + rename scheduler to heartbeat, [68a6551ed038a36330b2f098112829465a1c3c7a] + remove unnecessary is_running load, [76ccf7c00691f815c3ab0bede838c99252ba84f0] + execute_command(), [2a834d6c2fa4a15124d24ddbd12f667829e148ad] ++ Remove numerous clones(), [e5927f781a7e9517b9fa00a2d1a835d2774a9d26] ++ remove app_data param from generate_lock(), [1a8dab654a1fdbf351a72dc54fe3d1943355bba6] ++ combine get_filter methods, [356ea5549bb4877e9893fe0e1053e73c5a62e806] ++ FrameData refactors, [57781701ff14c553dfbafb965ee8a33ab44dd36f], [6e2f82db81caaa98ce4781fa15928eb9e246ace6] +### Tests ++ fix logs tests, [9b22f5da18e4bf92766a68a7f4cd61ad72724cfd] # v0.8.0 ### 2024-10-22 diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 49a3e96..de51655 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -314,7 +314,7 @@ fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc Date: Wed, 4 Dec 2024 10:10:44 +0000 Subject: [PATCH 32/44] refactor: update_container_stat combine is_alive() --- Cargo.lock | 28 +++++++------- src/docker_data/mod.rs | 86 ++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac8d64f..0042846 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "autocfg" @@ -151,7 +151,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror 2.0.3", + "thiserror 2.0.4", "tokio", "tokio-util", "tower-service", @@ -239,9 +239,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.21" +version = "4.5.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "69371e34337c4c984bbe322360c2547210bf632eb2814bbe78a6e87a2935bd2b" dependencies = [ "clap_builder", "clap_derive", @@ -249,9 +249,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "6e24c1b4099818523236a8ca881d2b45db98dadfb4625cf6608c12069fcbbde1" dependencies = [ "anstream", "anstyle", @@ -556,9 +556,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -1481,11 +1481,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.4", ] [[package]] @@ -1501,9 +1501,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061" dependencies = [ "proc-macro2", "quote", diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 03c6e51..87301a0 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -117,62 +117,58 @@ impl DockerData { spawn_id: SpawnId, spawns: Arc>>>, ) { - if state.is_alive() { - let id = spawn_id.get_id(); - let mut stream = docker - .stats( - id.get(), - Some(StatsOptions { - stream: false, - one_shot: false, - }), - ) - .take(1); + let id = spawn_id.get_id(); + let mut stream = docker + .stats( + id.get(), + Some(StatsOptions { + stream: false, + one_shot: false, + }), + ) + .take(1); - while let Some(Ok(stats)) = stream.next().await { - // Memory stats are only collected if the container is alive - is this the behaviour we want? - let mem_stat = if state.is_alive() { - let mem_cache = stats.memory_stats.stats.map_or(0, |i| match i { - MemoryStatsStats::V1(x) => x.inactive_file, - MemoryStatsStats::V2(x) => x.inactive_file, - }); + while let Some(Ok(stats)) = stream.next().await { + // 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_cache = stats.memory_stats.stats.map_or(0, |i| match i { + MemoryStatsStats::V1(x) => x.inactive_file, + MemoryStatsStats::V2(x) => x.inactive_file, + }); + ( Some( stats .memory_stats .usage .unwrap_or_default() .saturating_sub(mem_cache), - ) - } else { - None - }; + ), + Some(Self::calculate_usage(&stats)), + ) + } else { + (None, None) + }; - let mem_limit = stats.memory_stats.limit.unwrap_or_default(); + let mem_limit = stats.memory_stats.limit.unwrap_or_default(); - let op_key = stats + let op_key = stats + .networks + .as_ref() + .and_then(|networks| networks.keys().next().cloned()); + + let (rx, tx) = if let Some(key) = op_key { + stats .networks - .as_ref() - .and_then(|networks| networks.keys().next().cloned()); + .unwrap_or_default() + .get(&key) + .map_or((0, 0), |f| (f.rx_bytes, f.tx_bytes)) + } else { + (0, 0) + }; - let cpu_stats = if state.is_alive() { - Some(Self::calculate_usage(&stats)) - } else { - None - }; - let (rx, tx) = if let Some(key) = op_key { - 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(id, cpu_stats, mem_stat, mem_limit, rx, tx); - } + app_data + .lock() + .update_stats_by_id(id, cpu_stats, mem_stat, mem_limit, rx, tx); } spawns.lock().remove(&spawn_id); } From 096c6526a8bb6545f28e45aa9901e7135776f54f Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:41:36 +0000 Subject: [PATCH 33/44] docs: changelog --- CHANGELOG.md | 1 + Cargo.lock | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 049f758..a3362a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ + remove app_data param from generate_lock(), [1a8dab654a1fdbf351a72dc54fe3d1943355bba6] + combine get_filter methods, [356ea5549bb4877e9893fe0e1053e73c5a62e806] + FrameData refactors, [57781701ff14c553dfbafb965ee8a33ab44dd36f], [6e2f82db81caaa98ce4781fa15928eb9e246ace6] ++ update_container_stat combine is_alive(), [55cc746736f6863aedc5ad838744a983796244d8] ### Tests + fix logs tests, [9b22f5da18e4bf92766a68a7f4cd61ad72724cfd] diff --git a/Cargo.lock b/Cargo.lock index 0042846..92ba1a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1592,9 +1592,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", From 35aec5060fdbe606267be26656b4aeee43d50c02 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Wed, 4 Dec 2024 21:28:10 +0000 Subject: [PATCH 34/44] refactor: pass &FrameDate into draw_frame() --- src/app_data/mod.rs | 20 ++--- src/input_handler/mod.rs | 50 ++++++------ src/ui/draw_blocks.rs | 164 ++++++++++++++++++++++++--------------- src/ui/gui_state.rs | 7 +- src/ui/mod.rs | 74 ++++++++++-------- 5 files changed, 179 insertions(+), 136 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index aa08972..f355f2b 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -478,10 +478,11 @@ impl AppData { } /// Get Option of the current selected container's ports, sorted by private port - pub fn get_selected_ports(&mut self) -> Option<(&[ContainerPorts], State)> { - if let Some(item) = self.get_mut_selected_container() { - item.ports.sort_by(|a, b| a.private.cmp(&b.private)); - return Some((&item.ports, item.state)); + pub fn get_selected_ports(&self) -> Option<(Vec, State)> { + if let Some(item) = self.get_selected_container() { + let mut ports = item.ports.clone(); + ports.sort_by(|a, b| a.private.cmp(&b.private)); + return Some((ports, item.state)); } None } @@ -646,12 +647,12 @@ impl AppData { /// Chart data related methods /// Get mutable Option of the currently selected container chart data - pub fn get_chart_data(&mut self) -> Option<(CpuTuple, MemTuple)> { + pub fn get_chart_data(&self) -> Option<(CpuTuple, MemTuple)> { self.containers .state .selected() - .and_then(|i| self.containers.items.get_mut(i)) - .map(|i| i.get_chart_data()) + .and_then(|i| self.containers.items.get(i)) + .map(container_state::ContainerItem::get_chart_data) } /// Error related methods @@ -2180,8 +2181,7 @@ mod tests { private: 8001, public: None } - ] - .as_slice(), + ], State::Running(RunningState::Healthy), )) ); @@ -2193,7 +2193,7 @@ mod tests { assert_eq!( result, - Some((vec![].as_slice(), State::Running(RunningState::Healthy))) + Some((vec![], State::Running(RunningState::Healthy))) ); } diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index 6799aaa..4e05ad4 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -64,19 +64,17 @@ impl InputHandler { match message { InputMessages::ButtonPress(key) => self.button_press(key.0, key.1).await, InputMessages::MouseEvent(mouse_event) => { - if !self.gui_state.lock().status_contains(&[ - Status::Error, - Status::Help, - Status::DeleteConfirm, - Status::Filter, - ]) { + let status = self.gui_state.lock().get_status(); + let contains = |s: Status| status.contains(&s); + + if !contains(Status::Error) + | !contains(Status::Help) + | !contains(Status::DeleteConfirm) + | !contains(Status::Filter) + { self.mouse_press(mouse_event); } - let delete_confirm = self - .gui_state - .lock() - .status_contains(&[Status::DeleteConfirm]); - if delete_confirm { + if contains(Status::DeleteConfirm) { self.button_intersect(mouse_event).await; } } @@ -92,11 +90,9 @@ impl InputHandler { /// Send a quit message to docker, to abort all spawns, if an error is returned, set is_running to false here instead /// If gui_status is Error or Init, then just set the is_running to false immediately, for a quicker exit fn quit(&self) { - let error_init = self - .gui_state - .lock() - .status_contains(&[Status::Error, Status::Init]); - if !error_init { + let status = self.gui_state.lock().get_status(); + let contains = |s: Status| status.contains(&s); + if !contains(Status::Error) | !contains(Status::Init) { self.is_running .store(false, std::sync::atomic::Ordering::SeqCst); } @@ -235,10 +231,11 @@ impl InputHandler { /// Attempt to save the currently selected container logs to a file async fn s_key(&self) { - let log_status = Status::Logs; - let status = self.gui_state.lock().status_contains(&[log_status]); - if !status { - self.gui_state.lock().status_push(log_status); + let status = self.gui_state.lock().get_status(); + let contains = |s: Status| status.contains(&s); + + if !contains(Status::Logs) { + self.gui_state.lock().status_push(Status::Logs); let uuid = Uuid::new_v4(); GuiState::start_loading_animation(&self.gui_state, uuid); if self.save_logs().await.is_err() { @@ -248,7 +245,7 @@ impl InputHandler { Status::Error, ); } - self.gui_state.lock().status_del(log_status); + self.gui_state.lock().status_del(Status::Logs); self.gui_state.lock().stop_loading_animation(uuid); } } @@ -426,17 +423,14 @@ impl InputHandler { } /// Handle keyboard button events async fn button_press(&mut self, key_code: KeyCode, key_modifier: KeyModifiers) { - let contains_delete = self - .gui_state - .lock() - .status_contains(&[Status::DeleteConfirm]); - - let contains = |s: Status| self.gui_state.lock().status_contains(&[s]); + let status = self.gui_state.lock().get_status(); + let contains = |s: Status| status.contains(&s); let contains_error = contains(Status::Error); let contains_help = contains(Status::Help); let contains_exec = contains(Status::Exec); - let contains_filter: bool = contains(Status::Filter); + let contains_filter = contains(Status::Filter); + let contains_delete = contains(Status::DeleteConfirm); if !contains_exec { let is_c = || key_code == KeyCode::Char('c') || key_code == KeyCode::Char('C'); diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index cebfb02..08f4752 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -96,7 +96,7 @@ fn generate_block<'a>( .borders(Borders::ALL) .border_type(BorderType::Rounded) .title(title); - if fd.selected_panel == panel && !gui_state.lock().status_contains(&[Status::Filter]) { + if fd.selected_panel == panel && !fd.status.contains(&Status::Filter) { block = block.border_style(Style::default().fg(Color::LightCyan)); } block @@ -260,7 +260,7 @@ pub fn logs( gui_state: &Arc>, ) { let block = generate_block(area, fd, gui_state, SelectablePanel::Logs); - if fd.init { + if fd.status.contains(&Status::Init) { let paragraph = Paragraph::new(format!("parsing logs {}", fd.loading_icon)) .style(Style::default()) .block(block) @@ -274,7 +274,7 @@ pub fn logs( .alignment(Alignment::Center); f.render_widget(paragraph, area); } else { - let items = List::new(app_data.lock().get_logs()) + let items = List::new(logs) .block(block) .highlight_symbol(RIGHT_ARROW) .highlight_style(Style::default().add_modifier(Modifier::BOLD)); @@ -287,15 +287,8 @@ pub fn logs( } // Display the ports in a formatted list -pub fn ports( - f: &mut Frame, - area: Rect, - app_data: &Arc>, - max_lens: (usize, usize, usize), -) { - let mut data = app_data.lock(); - let ports = data.get_selected_ports(); - if let Some(ports) = ports { +pub fn ports(f: &mut Frame, area: Rect, fd: &FrameData) { + if let Some(ports) = fd.ports.as_ref() { let block = Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) @@ -307,7 +300,7 @@ pub fn ports( .add_modifier(Modifier::BOLD), )); - let (ip, private, public) = max_lens; + let (ip, private, public) = fd.port_max_lens; if ports.0.is_empty() { let text = match ports.1 { @@ -318,7 +311,6 @@ pub fn ports( .alignment(Alignment::Center) .block(block); f.render_widget(paragraph, area); - drop(data); } else { let mut output = vec![Line::from( Span::from(format!( @@ -327,7 +319,7 @@ pub fn ports( )) .fg(Color::Yellow), )]; - for item in ports.0 { + for item in &ports.0 { let fg = Color::White; let strings = item.get_all(); @@ -345,9 +337,8 @@ pub fn ports( } /// Draw the cpu + mem charts -pub fn chart(f: &mut Frame, area: Rect, app_data: &Arc>) { - let cpu_mem = app_data.lock().get_chart_data(); - if let Some((cpu, mem)) = cpu_mem { +pub fn chart(f: &mut Frame, area: Rect, fd: &FrameData) { + if let Some((cpu, mem)) = fd.chart_data.as_ref() { let area = Layout::default() .direction(Direction::Horizontal) .constraints(CONSTRAINT_50_50) @@ -489,10 +480,11 @@ pub fn filter_bar(area: Rect, frame: &mut Frame, fd: &FrameData) { /// Draw heading bar at top of program, always visible /// TODO Should separate into loading icon/headers/help functions +#[allow(clippy::too_many_lines)] pub fn heading_bar( area: Rect, frame: &mut Frame, - data: &FrameData, + fd: &FrameData, gui_state: &Arc>, ) { let block = |fg: Color| Block::default().style(Style::default().bg(Color::Magenta).fg(fg)); @@ -503,7 +495,7 @@ pub fn heading_bar( let header_block = |x: &Header| { let mut color = Color::Black; let mut suffix = ""; - if let Some((a, b)) = &data.sorted_by { + if let Some((a, b)) = &fd.sorted_by { if x == a { match b { SortedOrder::Asc => suffix = " ▲", @@ -536,26 +528,30 @@ pub fn heading_bar( // Meta data to iterate over to create blocks with correct widths let header_meta = [ - (Header::Name, data.columns.name.1), - (Header::State, data.columns.state.1), - (Header::Status, data.columns.status.1), - (Header::Cpu, data.columns.cpu.1), - (Header::Memory, data.columns.mem.1 + data.columns.mem.2 + 3), - (Header::Id, data.columns.id.1), - (Header::Image, data.columns.image.1), - (Header::Rx, data.columns.net_rx.1), - (Header::Tx, data.columns.net_tx.1), + (Header::Name, fd.columns.name.1), + (Header::State, fd.columns.state.1), + (Header::Status, fd.columns.status.1), + (Header::Cpu, fd.columns.cpu.1), + (Header::Memory, fd.columns.mem.1 + fd.columns.mem.2 + 3), + (Header::Id, fd.columns.id.1), + (Header::Image, fd.columns.image.1), + (Header::Rx, fd.columns.net_rx.1), + (Header::Tx, fd.columns.net_tx.1), ]; // Need to add widths to this - let suffix = if data.help_visible { "exit" } else { "show" }; + let suffix = if fd.status.contains(&Status::Help) { + "exit" + } else { + "show" + }; let info_text = format!("( h ) {suffix} help{MARGIN}",); let info_width = info_text.chars().count(); let column_width = usize::from(area.width).saturating_sub(info_width); let column_width = if column_width > 0 { column_width } else { 1 }; - let splits = if data.has_containers { + let splits = if fd.has_containers { vec![ Constraint::Max(4), Constraint::Max(column_width.try_into().unwrap_or_default()), @@ -571,11 +567,11 @@ pub fn heading_bar( .split(area); // Draw loading icon, or not, and a prefix with a single space - let loading_paragraph = Paragraph::new(format!("{:>2}", data.loading_icon)) + let loading_paragraph = Paragraph::new(format!("{:>2}", fd.loading_icon)) .block(block(Color::White)) .alignment(Alignment::Left); frame.render_widget(loading_paragraph, split_bar[0]); - if data.has_containers { + if fd.has_containers { let header_section_width = split_bar[1].width; let mut counter = 0; @@ -610,7 +606,7 @@ pub fn heading_bar( } // show/hide help - let color = if data.help_visible { + let color = if fd.status.contains(&Status::Help) { Color::Black } else { Color::White @@ -620,7 +616,7 @@ pub fn heading_bar( .alignment(Alignment::Right); // If no containers, don't display the headers, could maybe do this first? - let help_index = if data.has_containers { 2 } else { 0 }; + let help_index = if fd.has_containers { 2 } else { 0 }; frame.render_widget(help_paragraph, split_bar[help_index]); } @@ -1004,7 +1000,7 @@ pub fn error(f: &mut Frame, error: AppError, seconds: Option) { /// Draw info box in one of the 9 BoxLocations // TODO is this broken - I don't think so -pub fn info(f: &mut Frame, text: String, instant: Instant, gui_state: &Arc>) { +pub fn info(f: &mut Frame, text: String, instant: &Instant, gui_state: &Arc>) { let block = Block::default() .title("") .title_alignment(Alignment::Center) @@ -1084,7 +1080,7 @@ mod tests { }, app_error::AppError, tests::{gen_appdata, gen_container_summary, gen_containers}, - ui::{draw_frame, GuiState}, + ui::{draw_frame, GuiState, Status}, }; use super::{FrameData, ORANGE, VERSION}; @@ -1100,6 +1096,42 @@ mod tests { const BORDER_CHARS: [&str; 6] = ["╭", "╮", "─", "│", "╰", "╯"]; + impl From<(&Arc>, &Arc>)> for FrameData { + fn from(data: (&Arc>, &Arc>)) -> Self { + let (app_data, gui_data) = (data.0.lock(), data.1.lock()); + + // set max height for container section, needs +5 to deal with docker commands list and borders + let height = app_data.get_container_len(); + let height = if height < 12 { + u16::try_from(height + 5).unwrap_or_default() + } else { + 12 + }; + + let (filter_by, filter_term) = app_data.get_filter(); + Self { + chart_data: app_data.get_chart_data(), + columns: app_data.get_width(), + container_title: app_data.get_container_title(), + delete_confirm: gui_data.get_delete_container(), + filter_by, + filter_term: filter_term.cloned(), + has_containers: app_data.get_container_len() > 0, + has_error: app_data.get_error(), + height, + ports: app_data.get_selected_ports(), + port_max_lens: app_data.get_longest_port(), + info_text: gui_data.info_box_text.clone(), + is_loading: gui_data.is_loading(), + loading_icon: gui_data.get_loading().to_string(), + log_title: app_data.get_log_title(), + selected_panel: gui_data.get_selected_panel(), + sorted_by: app_data.get_sorted(), + status: gui_data.get_status(), + } + } + } + /// Generate state to be used in *most* gui tests fn test_setup(w: u16, h: u16, control_start: bool, container_start: bool) -> TuiTestSetup { let backend = TestBackend::new(w, h); @@ -1118,7 +1150,6 @@ mod tests { let app_data = Arc::new(Mutex::new(app_data)); let gui_state = Arc::new(Mutex::new(gui_state)); - let fd = FrameData::from((&app_data, &gui_state)); let area = Rect::new(0, 0, w, h); TuiTestSetup { @@ -1944,7 +1975,7 @@ mod tests { ]; let mut fd = FrameData::from((&setup.app_data, &setup.gui_state)); - fd.init = true; + fd.status.insert(Status::Init); setup .terminal @@ -1974,7 +2005,7 @@ mod tests { ]; let mut fd = FrameData::from((&setup.app_data, &setup.gui_state)); - fd.init = true; + fd.status.insert(Status::Init); setup .terminal .draw(|f| { @@ -2188,10 +2219,11 @@ mod tests { let (w, h) = (80, 10); let mut setup = test_setup(w, h, true, true); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::chart(f, setup.area, &setup.app_data); + super::chart(f, setup.area, &fd); }) .unwrap(); @@ -2238,11 +2270,12 @@ mod tests { let mut setup = test_setup(w, h, true, true); insert_chart_data(&setup); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::chart(f, setup.area, &setup.app_data); + super::chart(f, setup.area, &fd); }) .unwrap(); @@ -2285,11 +2318,12 @@ mod tests { insert_chart_data(&setup); setup.app_data.lock().containers.items[0].state = State::Paused; + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::chart(f, setup.area, &setup.app_data); + super::chart(f, setup.area, &fd); }) .unwrap(); @@ -2327,11 +2361,12 @@ mod tests { let mut setup = test_setup(w, h, true, true); insert_chart_data(&setup); setup.app_data.lock().containers.items[0].state = State::Dead; + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::chart(f, setup.area, &setup.app_data); + super::chart(f, setup.area, &fd); }) .unwrap(); @@ -2393,7 +2428,7 @@ mod tests { } } - fd.help_visible = true; + fd.status.insert(Status::Help); let expected = [" ( h ) exit help "]; setup .terminal @@ -2807,7 +2842,7 @@ mod tests { super::info( f, "test".to_owned(), - std::time::Instant::now(), + &std::time::Instant::now(), &setup.gui_state, ); }) @@ -3079,11 +3114,11 @@ mod tests { let mut setup = test_setup(w, h, true, true); setup.app_data.lock().containers.items[0].ports = vec![]; - let max_lens = setup.app_data.lock().get_longest_port(); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::ports(f, setup.area, &setup.app_data, max_lens); + super::ports(f, setup.area, &fd); }) .unwrap(); @@ -3124,11 +3159,12 @@ mod tests { // When state is "State::Running | State::Paused | State::Restarting, won't show "no ports" setup.app_data.lock().containers.items[0].state = State::Dead; - let max_lens = setup.app_data.lock().get_longest_port(); + + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::ports(f, setup.area, &setup.app_data, max_lens); + super::ports(f, setup.area, &fd); }) .unwrap(); @@ -3179,12 +3215,11 @@ mod tests { public: Some(8003), }); - let max_lens = setup.app_data.lock().get_longest_port(); - + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::ports(f, setup.area, &setup.app_data, max_lens); + super::ports(f, setup.area, &fd); }) .unwrap(); @@ -3233,12 +3268,12 @@ mod tests { fn test_draw_blocks_ports_container_state() { let (w, h) = (32, 8); let mut setup = test_setup(w, h, true, true); - let max_lens = setup.app_data.lock().get_longest_port(); + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::ports(f, setup.area, &setup.app_data, max_lens); + super::ports(f, setup.area, &fd); }) .unwrap(); @@ -3266,10 +3301,11 @@ mod tests { } setup.app_data.lock().containers.items[0].state = State::Paused; + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::ports(f, setup.area, &setup.app_data, max_lens); + super::ports(f, setup.area, &fd); }) .unwrap(); @@ -3286,10 +3322,11 @@ mod tests { } setup.app_data.lock().containers.items[0].state = State::Exited; + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - super::ports(f, setup.area, &setup.app_data, max_lens); + super::ports(f, setup.area, &fd); }) .unwrap(); @@ -3357,10 +3394,12 @@ mod tests { "│ │ ││ │ ││ │", "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯", ]; + + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - draw_frame(f, &setup.app_data, &setup.gui_state); + draw_frame(f, &setup.app_data, &setup.gui_state, &fd); }) .unwrap(); @@ -3421,10 +3460,11 @@ mod tests { "│ │ ││ │ ││ │", "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯", ]; + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - draw_frame(f, &setup.app_data, &setup.gui_state); + draw_frame(f, &setup.app_data, &setup.gui_state, &fd); }) .unwrap(); @@ -3475,10 +3515,11 @@ mod tests { "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯", " Esc clear ← by → Name Image Status All term: r_1 ", ]; + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - draw_frame(f, &setup.app_data, &setup.gui_state); + draw_frame(f, &setup.app_data, &setup.gui_state, &fd); }) .unwrap(); @@ -3543,10 +3584,11 @@ mod tests { "│ │ ││ │ ││ │", "╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────╯", ]; + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup .terminal .draw(|f| { - draw_frame(f, &setup.app_data, &setup.gui_state); + draw_frame(f, &setup.app_data, &setup.gui_state, &fd); }) .unwrap(); diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs index ce007e8..b7b1850 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -266,10 +266,9 @@ impl GuiState { self.delete_container = id; } - /// Check if the current gui_status contains any of the given status' - /// Don't really like this methodology for gui state, needs a re-think - pub fn status_contains(&self, status: &[Status]) -> bool { - status.iter().any(|i| self.status.contains(i)) + /// Return a copy of the Status HashSet + pub fn get_status(&self) -> HashSet { + self.status.clone() } /// Remove a gui_status into the current gui_status HashSet diff --git a/src/ui/mod.rs b/src/ui/mod.rs index de51655..4d22d86 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -11,6 +11,7 @@ use ratatui::{ Frame, Terminal, }; use std::{ + collections::HashSet, io::{self, Stdout, Write}, sync::{atomic::Ordering, Arc}, time::Duration, @@ -26,7 +27,10 @@ mod gui_state; pub use self::color_match::*; pub use self::gui_state::{DeleteButton, GuiState, SelectablePanel, Status}; use crate::{ - app_data::{AppData, Columns, ContainerId, FilterBy, Header, SortedOrder}, + app_data::{ + AppData, Columns, ContainerId, ContainerPorts, CpuTuple, FilterBy, Header, MemTuple, + SortedOrder, State, + }, app_error::AppError, exec::TerminalSize, input_handler::InputMessages, @@ -163,14 +167,15 @@ impl Ui { /// The loop for drawing the main UI to the terminal async fn gui_loop(&mut self) -> Result<(), AppError> { while self.is_running.load(Ordering::SeqCst) { - let exec = self.gui_state.lock().status_contains(&[Status::Exec]); + let fd = FrameData::from(&*self); + let exec = fd.status.contains(&Status::Exec); if exec { self.exec().await; } if self .terminal - .draw(|frame| draw_frame(frame, &self.app_data, &self.gui_state)) + .draw(|frame| draw_frame(frame, &self.app_data, &self.gui_state, &fd)) .is_err() { return Err(AppError::Terminal); @@ -206,11 +211,8 @@ impl Ui { /// Draw either the Error, or main oxker ui, to the terminal async fn draw_ui(&mut self) -> Result<(), AppError> { - let status_dockerconnect = self - .gui_state - .lock() - .status_contains(&[Status::DockerConnect]); - if status_dockerconnect { + let status = self.gui_state.lock().get_status(); + if status.contains(&Status::DockerConnect) { self.err_loop()?; } else { self.gui_loop().await?; @@ -221,8 +223,8 @@ impl Ui { /// Frequent data required by multiple framde drawing functions, can reduce mutex reads by placing it all in here #[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] pub struct FrameData { + chart_data: Option<(CpuTuple, MemTuple)>, columns: Columns, container_title: String, delete_confirm: Option, @@ -231,19 +233,20 @@ pub struct FrameData { has_containers: bool, has_error: Option, height: u16, - help_visible: bool, info_text: Option<(String, Instant)>, - init: bool, is_loading: bool, loading_icon: String, log_title: String, + port_max_lens: (usize, usize, usize), + ports: Option<(Vec, State)>, selected_panel: SelectablePanel, sorted_by: Option<(Header, SortedOrder)>, + status: HashSet, } -impl From<(&Arc>, &Arc>)> for FrameData { - fn from(data: (&Arc>, &Arc>)) -> Self { - let (app_data, gui_data) = (data.0.lock(), data.1.lock()); +impl From<&Ui> for FrameData { + fn from(ui: &Ui) -> Self { + let (app_data, gui_data) = (ui.app_data.lock(), ui.gui_state.lock()); // set max height for container section, needs +5 to deal with docker commands list and borders let height = app_data.get_container_len(); @@ -255,6 +258,7 @@ impl From<(&Arc>, &Arc>)> for FrameData { let (filter_by, filter_term) = app_data.get_filter(); Self { + chart_data: app_data.get_chart_data(), columns: app_data.get_width(), container_title: app_data.get_container_title(), delete_confirm: gui_data.get_delete_container(), @@ -263,24 +267,27 @@ impl From<(&Arc>, &Arc>)> for FrameData { has_containers: app_data.get_container_len() > 0, has_error: app_data.get_error(), height, - help_visible: gui_data.status_contains(&[Status::Help]), info_text: gui_data.info_box_text.clone(), - init: gui_data.status_contains(&[Status::Init]), is_loading: gui_data.is_loading(), loading_icon: gui_data.get_loading().to_string(), log_title: app_data.get_log_title(), + port_max_lens: app_data.get_longest_port(), + ports: app_data.get_selected_ports(), selected_panel: gui_data.get_selected_panel(), sorted_by: app_data.get_sorted(), + status: gui_data.get_status(), } } } /// Draw the main ui to a frame of the terminal -fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc>) { - let fd = FrameData::from((app_data, gui_state)); - let contains_filter = gui_state.lock().status_contains(&[Status::Filter]); - - let whole_constraints = if contains_filter { +fn draw_frame( + f: &mut Frame, + app_data: &Arc>, + gui_state: &Arc>, + fd: &FrameData, +) { + let whole_constraints = if fd.status.contains(&Status::Filter) { vec![Constraint::Max(1), Constraint::Min(1), Constraint::Max(1)] } else { vec![Constraint::Max(1), Constraint::Min(1)] @@ -320,15 +327,15 @@ fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc>, gui_state: &Arc Date: Wed, 4 Dec 2024 22:24:46 +0000 Subject: [PATCH 35/44] refactor: remove input_poll_rate from Ui, instead use const POLL_RATE --- src/ui/mod.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 4d22d86..fc3244c 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -37,11 +37,11 @@ use crate::{ }; pub const ORANGE: ratatui::style::Color = ratatui::style::Color::Rgb(255, 178, 36); +const POLL_RATE: Duration = std::time::Duration::from_millis(100); pub struct Ui { app_data: Arc>, gui_state: Arc>, - input_poll_rate: Duration, input_tx: Sender, is_running: Arc, now: Instant, @@ -75,7 +75,6 @@ impl Ui { app_data, cursor_position, gui_state, - input_poll_rate: std::time::Duration::from_millis(100), input_tx, is_running, now: Instant::now(), @@ -145,7 +144,7 @@ impl Ui { Ok(()) } - /// Use exeternal docker cli to exec into a container + /// Use external docker cli to exec into a container async fn exec(&mut self) { let exec_mode = self.gui_state.lock().get_exec_mode(); @@ -181,7 +180,7 @@ impl Ui { return Err(AppError::Terminal); } - if crossterm::event::poll(self.input_poll_rate).unwrap_or(false) { + if crossterm::event::poll(POLL_RATE).unwrap_or(false) { if let Ok(event) = event::read() { if let Event::Key(key) = event { if key.kind == event::KeyEventKind::Press { @@ -221,7 +220,7 @@ impl Ui { } } -/// Frequent data required by multiple framde drawing functions, can reduce mutex reads by placing it all in here +/// Frequent data required by multiple frame drawing functions, can reduce mutex reads by placing it all in here #[derive(Debug, Clone)] pub struct FrameData { chart_data: Option<(CpuTuple, MemTuple)>, From 64347919364813313f77c1bf5a687a3f6aaaa252 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:35:12 +0000 Subject: [PATCH 36/44] docs: changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3362a5..4035f05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ + Rust 1.83 linting, [751d997a3dac823e144ae62e6c1455676e50ddb8] ### Features -+ `--no-stderr` cli arg, closes #52, [c739637b91c8fa742a69f4d888678d7b3964678c] ++ `--no-stderr` cli arg, removes Standard error output from logs, closes #52, [c739637b91c8fa742a69f4d888678d7b3964678c] + ContainerPorts use ipaddr, [1b26997d25f748e0d452f41fe41791533046ecdf] ### Fixes @@ -27,6 +27,8 @@ + combine get_filter methods, [356ea5549bb4877e9893fe0e1053e73c5a62e806] + FrameData refactors, [57781701ff14c553dfbafb965ee8a33ab44dd36f], [6e2f82db81caaa98ce4781fa15928eb9e246ace6] + update_container_stat combine is_alive(), [55cc746736f6863aedc5ad838744a983796244d8] ++ remove `input_poll_rate` from `Ui`, instead use const `POLL_RATE`, [69f6c96b700b9fde5578ae204992a67986d456ab] ++ pass `&FrameDate` into `draw_frame()`, [35aec5060fdbe606267be26656b4aeee43d50c02] ### Tests + fix logs tests, [9b22f5da18e4bf92766a68a7f4cd61ad72724cfd] From d4906d33c26b75d92e7d80040c488faa90a257c6 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:25:16 +0000 Subject: [PATCH 37/44] fix: DockerData spawns insertion error. Previously incorrectly checking spawn entry in the hashmap, now use VacantEntry matching before spawning a tokio thread --- src/docker_data/mod.rs | 51 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 87301a0..437e7a9 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -178,18 +178,18 @@ impl DockerData { let all_ids = self.app_data.lock().get_all_id_state(); for (state, id) in all_ids { let spawn_id = SpawnId::Stats((id, self.binate)); - self.spawns - .lock() - .entry(spawn_id.clone()) - .or_insert_with(|| { - tokio::spawn(Self::update_container_stat( - Arc::clone(&self.app_data), - Arc::clone(&self.docker), - state, - spawn_id, - Arc::clone(&self.spawns), - )) - }); + + if let std::collections::hash_map::Entry::Vacant(spawns) = + self.spawns.lock().entry(spawn_id.clone()) + { + spawns.insert(tokio::spawn(Self::update_container_stat( + Arc::clone(&self.app_data), + Arc::clone(&self.docker), + state, + spawn_id, + Arc::clone(&self.spawns), + ))); + } } self.binate = self.binate.toggle(); } @@ -305,19 +305,20 @@ impl DockerData { self.update_all_containers().await; if let Some(container) = self.app_data.lock().get_selected_container() { let last_updated = container.last_updated; - self.spawns - .lock() - .entry(SpawnId::Log(container.id.clone())) - .or_insert_with(|| { - tokio::spawn(Self::update_log( - Arc::clone(&self.app_data), - Arc::clone(&self.docker), - container.id.clone(), - last_updated, - Arc::clone(&self.spawns), - self.args.std_err, - )) - }); + let spawn_id = SpawnId::Log(container.id.clone()); + // Only spawn if not already sapwned with a given id/binate pair + if let std::collections::hash_map::Entry::Vacant(spawns) = + self.spawns.lock().entry(spawn_id) + { + spawns.insert(tokio::spawn(Self::update_log( + Arc::clone(&self.app_data), + Arc::clone(&self.docker), + container.id.clone(), + last_updated, + Arc::clone(&self.spawns), + self.args.std_err, + ))); + } }; self.update_all_container_stats(); self.app_data.lock().sort_containers(); From a98624d8acb8ac473733db4500bca4353632fdda Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:37:48 +0000 Subject: [PATCH 38/44] docs: changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4035f05..952f639 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixes + update containerised Dockerfile, [0c6f53228f01196e352c2069383ba1e7a10950a8] + calculate_usage overflow, [5106a01f3dcb87ce5a8f1fb7bf49dc6b3c25d03e] ++ DockerData spawns insertion error, [d4906d33c26b75d92e7d80040c488faa90a257c6] ### Refactors + speed up docker logs init process, [8b9fe4246865441704ae12dff0938868a4fe6f81] From de76bc22936b124dcb9646f302f6cc14691dbb63 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:45:16 +0000 Subject: [PATCH 39/44] refactor: draw_block make TX & RX color consts, refactor filter_by_spans --- src/ui/draw_blocks.rs | 73 +++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 08f4752..13f2f18 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -43,6 +43,9 @@ const MARGIN: &str = " "; const RIGHT_ARROW: &str = "▶ "; const CIRCLE: &str = "⚪ "; +const COLOR_RX: Color = Color::Rgb(255, 233, 193); +const COLOR_TX: Color = Color::Rgb(205, 140, 140); + const CONSTRAINT_50_50: [Constraint; 2] = [Constraint::Percentage(50), Constraint::Percentage(50)]; const CONSTRAINT_100: [Constraint; 1] = [Constraint::Percentage(100)]; const CONSTRAINT_POPUP: [Constraint; 5] = [ @@ -80,6 +83,7 @@ fn generate_block<'a>( gui_state .lock() .update_region_map(Region::Panel(panel), area); + let mut title = match panel { SelectablePanel::Containers => { format!("{}{}", panel.title(), fd.container_title) @@ -140,7 +144,6 @@ fn format_containers<'a>(i: &ContainerItem, widths: &Columns) -> Line<'a> { let state_style = Style::default().fg(i.state.get_color()); let blue = Style::default().fg(Color::Blue); - // Truncate? Line::from(vec![ Span::styled( format!( @@ -202,11 +205,11 @@ fn format_containers<'a>(i: &ContainerItem, widths: &Columns) -> Line<'a> { ), Span::styled( format!("{:>width$}{MARGIN}", i.rx, width = widths.net_rx.1.into()), - Style::default().fg(Color::Rgb(255, 233, 193)), + Style::default().fg(COLOR_RX), ), Span::styled( format!("{:>width$}{MARGIN}", i.tx, width = widths.net_tx.1.into()), - Style::default().fg(Color::Rgb(205, 140, 140)), + Style::default().fg(COLOR_TX), ), ]) } @@ -250,8 +253,7 @@ pub fn containers( } } -// /// Draw the logs panel -// PREV WORKING +/// Draw the logs panel pub fn logs( app_data: &Arc>, area: Rect, @@ -286,7 +288,7 @@ pub fn logs( } } -// Display the ports in a formatted list +/// Display the ports in a formatted list pub fn ports(f: &mut Frame, area: Rect, fd: &FrameData) { if let Some(ports) = fd.ports.as_ref() { let block = Block::default() @@ -418,35 +420,22 @@ fn filter_by_spans(fd: &FrameData) -> [Span; 4] { let selected = Style::default().bg(Color::Gray).fg(Color::Black); let not_selected = Style::default().bg(Color::Reset).fg(Color::Reset); - // This should be refactored somehow let name = [" Name ", " Image ", " Status ", " All "]; + let mut filter_spans = [ + Span::styled(name[0], not_selected), + Span::styled(name[1], not_selected), + Span::styled(name[2], not_selected), + Span::styled(name[3], not_selected), + ]; + match fd.filter_by { - FilterBy::Name => [ - Span::styled(name[0], selected), - Span::styled(name[1], not_selected), - Span::styled(name[2], not_selected), - Span::styled(name[3], not_selected), - ], - FilterBy::Image => [ - Span::styled(name[0], not_selected), - Span::styled(name[1], selected), - Span::styled(name[2], not_selected), - Span::styled(name[3], not_selected), - ], - FilterBy::Status => [ - Span::styled(name[0], not_selected), - Span::styled(name[1], not_selected), - Span::styled(name[2], selected), - Span::styled(name[3], not_selected), - ], - FilterBy::All => [ - Span::styled(name[0], not_selected), - Span::styled(name[1], not_selected), - Span::styled(name[2], not_selected), - Span::styled(name[3], selected), - ], + FilterBy::Name => filter_spans[0] = Span::styled(name[0], selected), + FilterBy::Image => filter_spans[1] = Span::styled(name[1], selected), + FilterBy::Status => filter_spans[2] = Span::styled(name[2], selected), + FilterBy::All => filter_spans[3] = Span::styled(name[3], selected), } + filter_spans } /// Draw the filter bar @@ -513,6 +502,7 @@ pub fn heading_bar( let gen_header = |header: &Header, width: usize| { let block = header_block(header); + // TODO // Yes this is a mess, needs documenting correctly let text = format!( @@ -1080,7 +1070,10 @@ mod tests { }, app_error::AppError, tests::{gen_appdata, gen_container_summary, gen_containers}, - ui::{draw_frame, GuiState, Status}, + ui::{ + draw_blocks::{COLOR_RX, COLOR_TX}, + draw_frame, GuiState, Status, + }, }; use super::{FrameData, ORANGE, VERSION}; @@ -1556,11 +1549,11 @@ mod tests { } // rx column (1..=3, 92..=101) => { - assert_eq!(result_cell.fg, Color::Rgb(255, 233, 193)); + assert_eq!(result_cell.fg, COLOR_RX); } // tx column (1..=3, 102..=111) => { - assert_eq!(result_cell.fg, Color::Rgb(205, 140, 140)); + assert_eq!(result_cell.fg, COLOR_TX); } _ => assert_eq!(result_cell.fg, Color::Reset), } @@ -1630,11 +1623,11 @@ mod tests { } // rx column (1..=3, 92..=101) => { - assert_eq!(result_cell.fg, Color::Rgb(255, 233, 193)); + assert_eq!(result_cell.fg, COLOR_RX); } // tx column (1..=3, 102..=111) => { - assert_eq!(result_cell.fg, Color::Rgb(205, 140, 140)); + assert_eq!(result_cell.fg, COLOR_TX); } _ => assert_eq!(result_cell.fg, Color::Reset), } @@ -1797,11 +1790,11 @@ mod tests { } // rx column (1..=3, 95..=104) => { - assert_eq!(result_cell.fg, Color::Rgb(255, 233, 193)); + assert_eq!(result_cell.fg, COLOR_RX); } // tx column (1..=3, 105..=114) => { - assert_eq!(result_cell.fg, Color::Rgb(205, 140, 140)); + assert_eq!(result_cell.fg, COLOR_TX); } _ => { assert_eq!(result_cell.fg, Color::Reset); @@ -1861,11 +1854,11 @@ mod tests { } // rx column (1..=3, 104..=113) => { - assert_eq!(result_cell.fg, Color::Rgb(255, 233, 193)); + assert_eq!(result_cell.fg, COLOR_RX); } // tx column (1..=3, 114..=123) => { - assert_eq!(result_cell.fg, Color::Rgb(205, 140, 140)); + assert_eq!(result_cell.fg, COLOR_TX); } _ => assert_eq!(result_cell.fg, Color::Reset), } From 9c4f8910381b90b563da12eaba4b79cb60c40129 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:45:44 +0000 Subject: [PATCH 40/44] refactor: input_handler use a mouse_point variable --- src/input_handler/mod.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index 4e05ad4..1670410 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -436,7 +436,7 @@ impl InputHandler { let is_c = || key_code == KeyCode::Char('c') || key_code == KeyCode::Char('C'); let is_q = || key_code == KeyCode::Char('q') || key_code == KeyCode::Char('Q'); if key_modifier == KeyModifiers::CONTROL && is_c() || is_q() && !contains_filter { - // Always just quit on Ctrl + c/C or q/Q, unless in FIlter status active + // Always just quit on Ctrl + c/C or q/Q, unless in Filter status active self.quit(); } @@ -479,22 +479,13 @@ impl InputHandler { MouseEventKind::ScrollUp => self.previous(), MouseEventKind::ScrollDown => self.next(), MouseEventKind::Down(MouseButton::Left) => { - let header = self.gui_state.lock().header_intersect(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().header_intersect(mouse_point); if let Some(header) = header { self.sort(header); } - self.gui_state.lock().panel_intersect(Rect::new( - mouse_event.column, - mouse_event.row, - 1, - 1, - )); + self.gui_state.lock().panel_intersect(mouse_point); } _ => (), } From caf23be4a7faff99aaca80b081a02e4e0a372009 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:48:24 +0000 Subject: [PATCH 41/44] refactor: dead code removed --- src/app_error.rs | 1 - src/docker_data/mod.rs | 15 +++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/app_error.rs b/src/app_error.rs index 4afcf3f..e192ad8 100644 --- a/src/app_error.rs +++ b/src/app_error.rs @@ -2,7 +2,6 @@ use crate::app_data::DockerCommand; use std::fmt; /// app errors to set in global state -// #[allow(unused)] #[derive(Debug, Clone, Copy)] pub enum AppError { DockerCommand(DockerCommand), diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 437e7a9..b288af3 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -149,8 +149,6 @@ impl DockerData { (None, None) }; - let mem_limit = stats.memory_stats.limit.unwrap_or_default(); - let op_key = stats .networks .as_ref() @@ -166,9 +164,14 @@ impl DockerData { (0, 0) }; - app_data - .lock() - .update_stats_by_id(id, cpu_stats, mem_stat, mem_limit, rx, tx); + app_data.lock().update_stats_by_id( + id, + cpu_stats, + mem_stat, + stats.memory_stats.limit.unwrap_or_default(), + rx, + tx, + ); } spawns.lock().remove(&spawn_id); } @@ -306,7 +309,7 @@ impl DockerData { if let Some(container) = self.app_data.lock().get_selected_container() { let last_updated = container.last_updated; let spawn_id = SpawnId::Log(container.id.clone()); - // Only spawn if not already sapwned with a given id/binate pair + // Only spawn if not already spawned with a given id/binate pair if let std::collections::hash_map::Entry::Vacant(spawns) = self.spawns.lock().entry(spawn_id) { From e40e0a9569f952480481ab69452e6c502cb3957e Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:49:04 +0000 Subject: [PATCH 42/44] docs: changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 952f639..fe45572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,9 @@ + update_container_stat combine is_alive(), [55cc746736f6863aedc5ad838744a983796244d8] + remove `input_poll_rate` from `Ui`, instead use const `POLL_RATE`, [69f6c96b700b9fde5578ae204992a67986d456ab] + pass `&FrameDate` into `draw_frame()`, [35aec5060fdbe606267be26656b4aeee43d50c02] ++ dead code removed, [caf23be4a7faff99aaca80b081a02e4e0a372009] ++ input_handler, [9c4f8910381b90b563da12eaba4b79cb60c40129] ++ draw_block, [de76bc22936b124dcb9646f302f6cc14691dbb63] ### Tests + fix logs tests, [9b22f5da18e4bf92766a68a7f4cd61ad72724cfd] From 08b1e340548e49109d2499f1ff27df909d63e9b2 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 5 Dec 2024 20:14:11 +0000 Subject: [PATCH 43/44] fix: cargo fmt --- src/ui/draw_blocks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 13f2f18..d939255 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -502,7 +502,7 @@ pub fn heading_bar( let gen_header = |header: &Header, width: usize| { let block = header_block(header); - // TODO + // TODO // Yes this is a mess, needs documenting correctly let text = format!( From 894d8d823dffd4339fe54d72045a0a8ee805ed6f Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 5 Dec 2024 20:46:26 +0000 Subject: [PATCH 44/44] chore: release v0.9.0 --- .github/release-body.md | 40 +++++++++++++++++++++++---- CHANGELOG.md | 61 +++++++++++++++++++++-------------------- Cargo.lock | 2 +- Cargo.toml | 2 +- 4 files changed, 68 insertions(+), 37 deletions(-) diff --git a/.github/release-body.md b/.github/release-body.md index 679805e..166376e 100644 --- a/.github/release-body.md +++ b/.github/release-body.md @@ -1,14 +1,42 @@ -### 2024-10-22 +### 2024-12-05 ### Chores -+ dependencies updated, [ea877d23711b98ffd1108a74206d93d43482d44d], [af609c0dbf0caab4a073f822166de34999afb41b] -+ .devcontainer updated, [a9844436d003b84a3e9d8b600ea029b232566f3a] -+ create_release.sh updated, [c4943370f4a67f6c01c75a8a7f825912427666a2], [1389d8adbba75fef480eb1de09337eb7beb10ba3] ++ dependencies updated, [b78713579c4706d605e5b35fcd832610a0152294], [c6200e8f77f8bb1f0152cb9374029d15cc45df9d] ++ Rust 1.83 linting, [751d997a3dac823e144ae62e6c1455676e50ddb8] ### Features -+ Add Stderr output to logs, thanks [vincentmasse](https://github.com/vincentmasse), closes #48, merges #49, [b95c9311416cd0dbcfa5de90c23f3065bc2d6b17], [9936ad45e186ee431aade920674a2dc283937355], [289ede3f2531feeec56094a76bf34f4c69431bbe] ++ `--no-stderr` cli arg, removes Standard error output from logs, closes #52, [c739637b91c8fa742a69f4d888678d7b3964678c] ++ ContainerPorts use ipaddr, [1b26997d25f748e0d452f41fe41791533046ecdf] + +### Fixes ++ update containerised Dockerfile, [0c6f53228f01196e352c2069383ba1e7a10950a8] ++ calculate_usage overflow, [5106a01f3dcb87ce5a8f1fb7bf49dc6b3c25d03e] ++ DockerData spawns insertion error, [d4906d33c26b75d92e7d80040c488faa90a257c6] ### Refactors -+ Rust 1.82 linting, [c058c5a301cfd4e8d7a0079c4c3f8fdeae2803e5] ++ speed up docker logs init process, [8b9fe4246865441704ae12dff0938868a4fe6f81] ++ remove docker sleep, [f1562d1084336fe5be39894c93cb49107f0a4a6d] ++ dead code removed, [5ee48d5708fa6de0206c021db0bb611196e66fba], [ba6a95241389f99d504ee4bf3e87e19006f12e49], [f0b1145651625ad4e577d79baaf902d4d3bc0579] ++ input_handler, [7f4238349525c01ae9fb8b1f6c0946e5364dd55e] ++ statefulList get_state_title, [2d540b0e2210cc04d73035ec59211ffc739174f6] ++ statefulList next/previous, [7bb2bef28d90ebc58da86a0365a1904a0c32dffe] ++ help_box closure fn, [2860426d57a4458fcee49a2fd20e8e7bb9e71fb5] ++ use check_sub for sleep calculations, [fe3696e5576739d8b033d9e748b5ea696c4b4e4f] ++ rename scheduler to heartbeat, [68a6551ed038a36330b2f098112829465a1c3c7a] ++ remove unnecessary is_running load, [76ccf7c00691f815c3ab0bede838c99252ba84f0] ++ execute_command(), [2a834d6c2fa4a15124d24ddbd12f667829e148ad] ++ Remove numerous clones(), [e5927f781a7e9517b9fa00a2d1a835d2774a9d26] ++ remove app_data param from generate_lock(), [1a8dab654a1fdbf351a72dc54fe3d1943355bba6] ++ combine get_filter methods, [356ea5549bb4877e9893fe0e1053e73c5a62e806] ++ FrameData refactors, [57781701ff14c553dfbafb965ee8a33ab44dd36f], [6e2f82db81caaa98ce4781fa15928eb9e246ace6] ++ update_container_stat combine is_alive(), [55cc746736f6863aedc5ad838744a983796244d8] ++ remove `input_poll_rate` from `Ui`, instead use const `POLL_RATE`, [69f6c96b700b9fde5578ae204992a67986d456ab] ++ pass `&FrameDate` into `draw_frame()`, [35aec5060fdbe606267be26656b4aeee43d50c02] ++ dead code removed, [caf23be4a7faff99aaca80b081a02e4e0a372009] ++ input_handler, [9c4f8910381b90b563da12eaba4b79cb60c40129] ++ draw_block, [de76bc22936b124dcb9646f302f6cc14691dbb63] + +### Tests ++ fix logs tests, [9b22f5da18e4bf92766a68a7f4cd61ad72724cfd] see CHANGELOG.md for more details diff --git a/CHANGELOG.md b/CHANGELOG.md index fe45572..05e5d8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,41 +1,44 @@ +# v0.9.0 +### 2024-12-05 + ### Chores -+ dependencies updated, [b78713579c4706d605e5b35fcd832610a0152294], [c6200e8f77f8bb1f0152cb9374029d15cc45df9d] -+ Rust 1.83 linting, [751d997a3dac823e144ae62e6c1455676e50ddb8] ++ dependencies updated, [b7871357](https://github.com/mrjackwills/oxker/commit/b78713579c4706d605e5b35fcd832610a0152294), [c6200e8f](https://github.com/mrjackwills/oxker/commit/c6200e8f77f8bb1f0152cb9374029d15cc45df9d) ++ Rust 1.83 linting, [751d997a](https://github.com/mrjackwills/oxker/commit/751d997a3dac823e144ae62e6c1455676e50ddb8) ### Features -+ `--no-stderr` cli arg, removes Standard error output from logs, closes #52, [c739637b91c8fa742a69f4d888678d7b3964678c] -+ ContainerPorts use ipaddr, [1b26997d25f748e0d452f41fe41791533046ecdf] ++ `--no-stderr` cli arg, removes Standard error output from logs, closes [#52](https://github.com/mrjackwills/oxker/issues/52), [c739637b](https://github.com/mrjackwills/oxker/commit/c739637b91c8fa742a69f4d888678d7b3964678c) ++ ContainerPorts use ipaddr, [1b26997d](https://github.com/mrjackwills/oxker/commit/1b26997d25f748e0d452f41fe41791533046ecdf) ### Fixes -+ update containerised Dockerfile, [0c6f53228f01196e352c2069383ba1e7a10950a8] -+ calculate_usage overflow, [5106a01f3dcb87ce5a8f1fb7bf49dc6b3c25d03e] -+ DockerData spawns insertion error, [d4906d33c26b75d92e7d80040c488faa90a257c6] ++ update containerised Dockerfile, [0c6f5322](https://github.com/mrjackwills/oxker/commit/0c6f53228f01196e352c2069383ba1e7a10950a8) ++ calculate_usage overflow, [5106a01f](https://github.com/mrjackwills/oxker/commit/5106a01f3dcb87ce5a8f1fb7bf49dc6b3c25d03e) ++ DockerData spawns insertion error, [d4906d33](https://github.com/mrjackwills/oxker/commit/d4906d33c26b75d92e7d80040c488faa90a257c6) ### Refactors -+ speed up docker logs init process, [8b9fe4246865441704ae12dff0938868a4fe6f81] -+ remove docker sleep, [f1562d1084336fe5be39894c93cb49107f0a4a6d] -+ dead code removed, [5ee48d5708fa6de0206c021db0bb611196e66fba], [ba6a95241389f99d504ee4bf3e87e19006f12e49], [f0b1145651625ad4e577d79baaf902d4d3bc0579] -+ input_handler, [7f4238349525c01ae9fb8b1f6c0946e5364dd55e] -+ statefulList get_state_title, [2d540b0e2210cc04d73035ec59211ffc739174f6] -+ statefulList next/previous, [7bb2bef28d90ebc58da86a0365a1904a0c32dffe] -+ help_box closure fn, [2860426d57a4458fcee49a2fd20e8e7bb9e71fb5] -+ use check_sub for sleep calculations, [fe3696e5576739d8b033d9e748b5ea696c4b4e4f] -+ rename scheduler to heartbeat, [68a6551ed038a36330b2f098112829465a1c3c7a] -+ remove unnecessary is_running load, [76ccf7c00691f815c3ab0bede838c99252ba84f0] -+ execute_command(), [2a834d6c2fa4a15124d24ddbd12f667829e148ad] -+ Remove numerous clones(), [e5927f781a7e9517b9fa00a2d1a835d2774a9d26] -+ remove app_data param from generate_lock(), [1a8dab654a1fdbf351a72dc54fe3d1943355bba6] -+ combine get_filter methods, [356ea5549bb4877e9893fe0e1053e73c5a62e806] -+ FrameData refactors, [57781701ff14c553dfbafb965ee8a33ab44dd36f], [6e2f82db81caaa98ce4781fa15928eb9e246ace6] -+ update_container_stat combine is_alive(), [55cc746736f6863aedc5ad838744a983796244d8] -+ remove `input_poll_rate` from `Ui`, instead use const `POLL_RATE`, [69f6c96b700b9fde5578ae204992a67986d456ab] -+ pass `&FrameDate` into `draw_frame()`, [35aec5060fdbe606267be26656b4aeee43d50c02] -+ dead code removed, [caf23be4a7faff99aaca80b081a02e4e0a372009] -+ input_handler, [9c4f8910381b90b563da12eaba4b79cb60c40129] -+ draw_block, [de76bc22936b124dcb9646f302f6cc14691dbb63] ++ speed up docker logs init process, [8b9fe424](https://github.com/mrjackwills/oxker/commit/8b9fe4246865441704ae12dff0938868a4fe6f81) ++ remove docker sleep, [f1562d10](https://github.com/mrjackwills/oxker/commit/f1562d1084336fe5be39894c93cb49107f0a4a6d) ++ dead code removed, [5ee48d57](https://github.com/mrjackwills/oxker/commit/5ee48d5708fa6de0206c021db0bb611196e66fba), [ba6a9524](https://github.com/mrjackwills/oxker/commit/ba6a95241389f99d504ee4bf3e87e19006f12e49), [f0b11456](https://github.com/mrjackwills/oxker/commit/f0b1145651625ad4e577d79baaf902d4d3bc0579) ++ input_handler, [7f423834](https://github.com/mrjackwills/oxker/commit/7f4238349525c01ae9fb8b1f6c0946e5364dd55e) ++ statefulList get_state_title, [2d540b0e](https://github.com/mrjackwills/oxker/commit/2d540b0e2210cc04d73035ec59211ffc739174f6) ++ statefulList next/previous, [7bb2bef2](https://github.com/mrjackwills/oxker/commit/7bb2bef28d90ebc58da86a0365a1904a0c32dffe) ++ help_box closure fn, [2860426d](https://github.com/mrjackwills/oxker/commit/2860426d57a4458fcee49a2fd20e8e7bb9e71fb5) ++ use check_sub for sleep calculations, [fe3696e5](https://github.com/mrjackwills/oxker/commit/fe3696e5576739d8b033d9e748b5ea696c4b4e4f) ++ rename scheduler to heartbeat, [68a6551e](https://github.com/mrjackwills/oxker/commit/68a6551ed038a36330b2f098112829465a1c3c7a) ++ remove unnecessary is_running load, [76ccf7c0](https://github.com/mrjackwills/oxker/commit/76ccf7c00691f815c3ab0bede838c99252ba84f0) ++ execute_command(), [2a834d6c](https://github.com/mrjackwills/oxker/commit/2a834d6c2fa4a15124d24ddbd12f667829e148ad) ++ Remove numerous clones(), [e5927f78](https://github.com/mrjackwills/oxker/commit/e5927f781a7e9517b9fa00a2d1a835d2774a9d26) ++ remove app_data param from generate_lock(), [1a8dab65](https://github.com/mrjackwills/oxker/commit/1a8dab654a1fdbf351a72dc54fe3d1943355bba6) ++ combine get_filter methods, [356ea554](https://github.com/mrjackwills/oxker/commit/356ea5549bb4877e9893fe0e1053e73c5a62e806) ++ FrameData refactors, [57781701](https://github.com/mrjackwills/oxker/commit/57781701ff14c553dfbafb965ee8a33ab44dd36f), [6e2f82db](https://github.com/mrjackwills/oxker/commit/6e2f82db81caaa98ce4781fa15928eb9e246ace6) ++ update_container_stat combine is_alive(), [55cc7467](https://github.com/mrjackwills/oxker/commit/55cc746736f6863aedc5ad838744a983796244d8) ++ remove `input_poll_rate` from `Ui`, instead use const `POLL_RATE`, [69f6c96b](https://github.com/mrjackwills/oxker/commit/69f6c96b700b9fde5578ae204992a67986d456ab) ++ pass `&FrameDate` into `draw_frame()`, [35aec506](https://github.com/mrjackwills/oxker/commit/35aec5060fdbe606267be26656b4aeee43d50c02) ++ dead code removed, [caf23be4](https://github.com/mrjackwills/oxker/commit/caf23be4a7faff99aaca80b081a02e4e0a372009) ++ input_handler, [9c4f8910](https://github.com/mrjackwills/oxker/commit/9c4f8910381b90b563da12eaba4b79cb60c40129) ++ draw_block, [de76bc22](https://github.com/mrjackwills/oxker/commit/de76bc22936b124dcb9646f302f6cc14691dbb63) ### Tests -+ fix logs tests, [9b22f5da18e4bf92766a68a7f4cd61ad72724cfd] ++ fix logs tests, [9b22f5da](https://github.com/mrjackwills/oxker/commit/9b22f5da18e4bf92766a68a7f4cd61ad72724cfd) # v0.8.0 ### 2024-10-22 diff --git a/Cargo.lock b/Cargo.lock index 92ba1a3..1da5da6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1050,7 +1050,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "oxker" -version = "0.8.0" +version = "0.9.0" dependencies = [ "anyhow", "bollard", diff --git a/Cargo.toml b/Cargo.toml index 90b99c6..e9cc624 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxker" -version = "0.8.0" +version = "0.9.0" edition = "2021" authors = ["Jack Wills "] description = "A simple tui to view & control docker containers"