From 17caed16c5f608c21f777b1d756168df585c494b Mon Sep 17 00:00:00 2001 From: Joseph Guhlin Date: Thu, 28 Nov 2024 16:37:45 +1300 Subject: [PATCH 1/8] Getting a ratatui interface setup! Update app --- fakeminimap2/Cargo.lock | 932 ++++++++++++++--------------- fakeminimap2/Cargo.toml | 4 + fakeminimap2/src/channels.rs | 26 +- fakeminimap2/src/cli.rs | 7 +- fakeminimap2/src/main.rs | 42 +- fakeminimap2/src/rayon.rs | 16 +- fakeminimap2/src/state.rs | 228 +++++++ fakeminimap2/src/ui.rs | 39 ++ fakeminimap2/src/ui/app_display.rs | 79 +++ 9 files changed, 879 insertions(+), 494 deletions(-) create mode 100644 fakeminimap2/src/state.rs create mode 100644 fakeminimap2/src/ui.rs create mode 100644 fakeminimap2/src/ui/app_display.rs diff --git a/fakeminimap2/Cargo.lock b/fakeminimap2/Cargo.lock index 936ebb5..113d9a9 100644 --- a/fakeminimap2/Cargo.lock +++ b/fakeminimap2/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + [[package]] name = "adler2" version = "2.0.0" @@ -17,6 +26,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" + [[package]] name = "anstream" version = "0.6.18" @@ -53,7 +68,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -63,40 +78,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] -name = "bindgen" -version = "0.69.5" +name = "autocfg" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", -] +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] -name = "bio-types" -version = "1.0.4" +name = "backtrace" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dcf54f8b7f51450207d54780bab09c05f30b8b0caa991545082842e466ad7e" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ - "derive-new 0.6.0", - "lazy_static", - "regex", - "strum_macros", - "thiserror 1.0.69", + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", ] [[package]] @@ -105,6 +108,31 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bon" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e47d5c63335658326076cf7c81795af665c534ea552da69526d6cef51b12ed9" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b162272b6d55562ea30cc937d74ef4d07399e507bfd6eb3860f6a845c7264eef" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "buffer-redux" version = "1.0.2" @@ -121,10 +149,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] -name = "byteorder" -version = "1.5.0" +name = "bytes" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "bzip2" @@ -147,6 +175,21 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.2.1" @@ -158,32 +201,12 @@ dependencies = [ "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.5.21" @@ -224,21 +247,32 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" -[[package]] -name = "cmake" -version = "0.1.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" -dependencies = [ - "cc", -] - [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "colorsys" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54261aba646433cb567ec89844be4c4825ca92a4f8afba52fc4dd88436e31bbd" + +[[package]] +name = "compact_str" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -305,44 +339,71 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] -name = "custom_derive" -version = "0.1.7" +name = "crossterm" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] [[package]] -name = "derive-new" -version = "0.6.0" +name = "crossterm_winapi" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ - "proc-macro2", - "quote", - "syn", + "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 = "derive-new" -version = "0.7.0" +name = "darling_core" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ + "fnv", + "ident_case", "proc-macro2", "quote", + "strsim", "syn", ] [[package]] -name = "displaydoc" -version = "0.2.5" +name = "darling_macro" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "proc-macro2", + "darling_core", "quote", "syn", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "either" version = "1.13.0" @@ -372,18 +433,38 @@ dependencies = [ "log", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fakeminimap2" version = "0.1.0" dependencies = [ "clap", "crossbeam", + "crossterm", "env_logger", "humantime", "log", "minimap2", "needletail", + "ratatui", "rayon", + "tachyonfx", + "tokio", ] [[package]] @@ -397,28 +478,33 @@ dependencies = [ ] [[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "fs-utils" -version = "1.1.4" +name = "foldhash" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc7a9dc005c944c98a935e7fd626faf5bf7e5a609f94bc13e42fc4a02e52593" -dependencies = [ - "quick-error", -] +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" [[package]] -name = "glob" -version = "0.3.1" +name = "gimli" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "heck" @@ -427,17 +513,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "hts-sys" -version = "2.1.4" +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f348d14cb4e50444e39fcd6b00302fe2ed2bc88094142f6278391d349a386d" -dependencies = [ - "bindgen", - "cc", - "fs-utils", - "glob", - "libz-sys", -] +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "humantime" @@ -446,150 +525,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "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" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "icu_provider" -version = "1.5.0" +name = "indoc" +version = "2.0.5" 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", -] +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] -name = "icu_provider_macros" -version = "1.5.0" +name = "instability" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +checksum = "b829f37dead9dc39df40c2d3376c179fdfd2ac771f53f55d3c30dc096a3c0c6e" dependencies = [ + "darling", + "indoc", + "pretty_assertions", "proc-macro2", "quote", "syn", ] -[[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]] -name = "ieee754" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c" - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -598,13 +558,19 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + [[package]] name = "jobserver" version = "0.1.32" @@ -614,33 +580,11 @@ dependencies = [ "libc", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" -version = "0.2.165" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb4d3d38eab6c5239a362fa8bae48c03baf980a6e7079f063942d563ef3533e" - -[[package]] -name = "libloading" -version = "0.8.5" +version = "0.2.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" -dependencies = [ - "cfg-if", - "windows-targets", -] +checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" [[package]] name = "liblzma" @@ -669,23 +613,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", - "cmake", "libc", "pkg-config", "vcpkg", ] [[package]] -name = "linear-map" -version = "1.2.0" +name = "linux-raw-sys" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] -name = "litemap" -version = "0.7.4" +name = "lock_api" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] [[package]] name = "log" @@ -694,16 +641,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] -name = "memchr" -version = "2.7.4" +name = "lru" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "memchr" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minimap2" @@ -712,7 +662,6 @@ dependencies = [ "libc", "minimap2-sys", "needletail", - "rust-htslib", ] [[package]] @@ -733,6 +682,19 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "needletail" version = "0.6.0" @@ -748,29 +710,48 @@ dependencies = [ ] [[package]] -name = "newtype_derive" -version = "0.1.6" +name = "object" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8cd24d9f185bb7223958d8c1ff7a961b74b1953fd05dba7cc568a63b3861ec" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ - "rustc_version", + "memchr", ] [[package]] -name = "nom" -version = "7.1.3" +name = "parking_lot" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ - "memchr", - "minimal-lexical", + "lock_api", + "parking_lot_core", ] [[package]] -name = "percent-encoding" -version = "2.3.1" +name = "parking_lot_core" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pkg-config" @@ -778,6 +759,26 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -787,12 +788,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.37" @@ -802,6 +797,27 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools", + "lru", + "paste", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + [[package]] name = "rayon" version = "1.10.0" @@ -822,6 +838,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.11.1" @@ -852,79 +877,83 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "rust-htslib" -version = "0.48.0" +name = "rustc-demangle" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8313d0733b0a2782231fd7259e8f0873cb2542657719e06179811f86f8f797af" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ - "bio-types", - "byteorder", - "custom_derive", - "derive-new 0.7.0", - "hts-sys", - "ieee754", - "lazy_static", + "bitflags", + "errno", "libc", - "libz-sys", - "linear-map", - "newtype_derive", - "regex", - "thiserror 2.0.3", - "url", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "rustversion" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] -name = "rustc_version" -version = "0.1.7" +name = "ryu" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" -dependencies = [ - "semver", -] +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] -name = "rustversion" -version = "1.0.18" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "semver" -version = "0.1.20" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "serde" -version = "1.0.215" +name = "signal-hook" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ - "serde_derive", + "libc", + "signal-hook-registry", ] [[package]] -name = "serde_derive" -version = "1.0.215" +name = "signal-hook-mio" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ - "proc-macro2", - "quote", - "syn", + "libc", + "mio", + "signal-hook", ] [[package]] -name = "shlex" -version = "1.3.0" +name = "signal-hook-registry" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simple-easing" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832ddd7df0d98d6fd93b973c330b7c8e0742d5cb8f1afc7dea89dba4d2531aa1" [[package]] name = "smallvec" @@ -933,10 +962,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] -name = "stable_deref_trait" -version = "1.2.0" +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" @@ -944,6 +983,15 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + [[package]] name = "strum_macros" version = "0.26.4" @@ -969,66 +1017,46 @@ dependencies = [ ] [[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror" -version = "1.0.69" +name = "tachyonfx" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "a7bb70be127291790236fd45169b17a3be4aeb1902c8691f23fd02f49d174b7b" dependencies = [ - "thiserror-impl 1.0.69", + "bon", + "colorsys", + "ratatui", + "simple-easing", ] [[package]] -name = "thiserror" -version = "2.0.3" +name = "tokio" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ - "thiserror-impl 2.0.3", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", ] [[package]] -name = "thiserror-impl" -version = "2.0.3" +name = "tokio-macros" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "unicode-ident" version = "1.0.14" @@ -1036,27 +1064,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] -name = "url" -version = "2.5.4" +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", + "itertools", + "unicode-segmentation", + "unicode-width 0.1.14", ] [[package]] -name = "utf16_iter" -version = "1.0.5" +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] -name = "utf8_iter" -version = "1.0.4" +name = "unicode-width" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "utf8parse" @@ -1070,6 +1104,43 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -1144,80 +1215,7 @@ 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 = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -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" +name = "yansi" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/fakeminimap2/Cargo.toml b/fakeminimap2/Cargo.toml index f10b4c8..c52cadd 100644 --- a/fakeminimap2/Cargo.toml +++ b/fakeminimap2/Cargo.toml @@ -14,6 +14,10 @@ log = "0.4.22" env_logger = "0.11.5" clap = { version = "4.5.21", features = ["derive"] } rayon = "1.10.0" +ratatui = "0.29.0" +tokio = { version = "1", features = ["full"] } +crossterm = "0.28.1" +tachyonfx = "0.9.3" [profile.release] opt-level = 3 diff --git a/fakeminimap2/src/channels.rs b/fakeminimap2/src/channels.rs index 97a3495..bb802e0 100644 --- a/fakeminimap2/src/channels.rs +++ b/fakeminimap2/src/channels.rs @@ -4,6 +4,9 @@ use needletail::{parse_fastx_file, FastxReader}; use std::{error::Error, path::Path, sync::Arc, time::Duration}; +use crate::state::QuerySequence; + + /// We use a worker queue to pass around work between threads. /// We do it this way to be generic over the type. enum WorkQueue { @@ -25,6 +28,9 @@ pub(crate) fn map_with_channels( target_file: impl AsRef, query_file: impl AsRef, threads: usize, + + // UI Stuff + dispatcher_tx: tokio::sync::mpsc::UnboundedSender, ) -> Result<(), Box> { // Aligner gets created using the build pattern. // Once .with_index is called, the aligner is set to "Built" and can no longer be changed. @@ -56,21 +62,15 @@ pub(crate) fn map_with_channels( let shutdown = Arc::clone(&shutdown); let aligner = Arc::clone(&aligner); - let handle = std::thread::spawn(move || { - worker( - work_queue, - results_queue, - shutdown, - aligner, - ) - }); + let handle = + std::thread::spawn(move || worker(work_queue, results_queue, shutdown, aligner)); jh.push(handle); } // Now that the threads are running, read the input file and push the work to the queue - let mut reader: Box = parse_fastx_file(query_file) - .unwrap_or_else(|_| panic!("Can't find query FASTA file")); + let mut reader: Box = + parse_fastx_file(query_file).unwrap_or_else(|_| panic!("Can't find query FASTA file")); // I just do this in the main thread, but you can split threads let backoff = crossbeam::utils::Backoff::new(); @@ -81,6 +81,12 @@ pub(crate) fn map_with_channels( // If we have an error, it's 99% because the queue is full backoff.snooze(); } + + // Oh and add it to the UI (this should be first, but trying to keep multithreading and UI separate) + let _ = dispatcher_tx.send(crate::state::Action::AddQuerySequence( + QuerySequence::new(std::str::from_utf8(record.id()).unwrap().to_string(), record.seq().to_vec()), + )); + println!("Sent query sequence"); } // Set the shutdown flag diff --git a/fakeminimap2/src/cli.rs b/fakeminimap2/src/cli.rs index 5a25971..e56f25a 100644 --- a/fakeminimap2/src/cli.rs +++ b/fakeminimap2/src/cli.rs @@ -3,7 +3,10 @@ use std::path::PathBuf; use clap::{Parser, ValueEnum}; #[derive(Parser, Debug)] -#[command(name = "fakeminimap2", about = "An example of how to use the minimap2 crate with multithreading")] +#[command( + name = "fakeminimap2", + about = "An example of how to use the minimap2 crate with multithreading" +)] pub(crate) struct Cli { /// The target file to align to (e.g. a reference genome - can be in FASTA, FASTQ, or mmi format) pub target: PathBuf, @@ -30,4 +33,4 @@ pub(crate) enum Method { pub(crate) fn parse_args() -> Cli { Cli::parse() -} \ No newline at end of file +} diff --git a/fakeminimap2/src/main.rs b/fakeminimap2/src/main.rs index a6fa919..9ed0671 100644 --- a/fakeminimap2/src/main.rs +++ b/fakeminimap2/src/main.rs @@ -2,7 +2,6 @@ /// Although mpsc is also available in the standard library. /// /// For logging, pass in RUST_LOG=debug or RUST_LOG=trace to see more information. RUST_LOG=info is also supported. - // CLI interface mod cli; @@ -10,17 +9,42 @@ mod cli; mod channels; // I prefer using channels over rayon, but rayon is simpler to use mod rayon; -fn main() { +// UI Stuff +mod ui; +use tokio::sync::mpsc; + +// Ignore the tokio stuff, it's just for visualization and interaction! +#[tokio::main] +async fn main() { env_logger::init(); + // Parse command line arguments let args = cli::parse_args(); + + // UI Stuff + let (dispatcher_tx, dispatcher_rx) = mpsc::unbounded_channel::(); + let (ui_tx, ui_rx) = mpsc::unbounded_channel::(); - match args.method.unwrap_or_default() { - cli::Method::Channels => { - channels::map_with_channels(args.target, args.query, args.threads).expect("Error mapping with channels"); - } - cli::Method::Rayon => { - rayon::map(args.target, args.query, args.threads).expect("Error mapping with rayon"); - } + { + let dispatcher_tx = dispatcher_tx.clone(); + let handle = std::thread::spawn(move || { + match args.method.unwrap_or_default() { + cli::Method::Channels => { + channels::map_with_channels(args.target, args.query, args.threads, dispatcher_tx.clone()) + .expect("Error mapping with channels"); + } + cli::Method::Rayon => { + rayon::map(args.target, args.query, args.threads, dispatcher_tx.clone()).expect("Error mapping with rayon"); + } + } + }); } + + // Runs the UI Loop + tokio::join!( + state::start_dispatcher(dispatcher_tx.clone(), dispatcher_rx, ui_tx), + ui::main_loop(dispatcher_tx.clone(), ui_rx), + ); } + +mod state; diff --git a/fakeminimap2/src/rayon.rs b/fakeminimap2/src/rayon.rs index e49b10e..25aa8d3 100644 --- a/fakeminimap2/src/rayon.rs +++ b/fakeminimap2/src/rayon.rs @@ -1,14 +1,16 @@ use std::{error::Error, path::Path}; -use rayon::prelude::*; -use needletail::{parse_fastx_file, FastxReader}; use minimap2::*; - +use needletail::{parse_fastx_file, FastxReader}; +use rayon::prelude::*; pub(crate) fn map( target_file: impl AsRef, query_file: impl AsRef, threads: usize, + + // UI Stuff + dispatcher_tx: tokio::sync::mpsc::UnboundedSender, ) -> Result<(), Box> { // Set the number of threads to use rayon::ThreadPoolBuilder::new() @@ -39,7 +41,9 @@ pub(crate) fn map( let results: Vec> = queries .par_iter() .map(|(id, seq)| { - aligner.map(&seq, false, false, None, None, Some(&id)).expect("Error mapping") + aligner + .map(&seq, false, false, None, None, Some(&id)) + .expect("Error mapping") }) .collect(); @@ -48,6 +52,6 @@ pub(crate) fn map( // Count total number of alignments let total_alignments: usize = results.iter().map(|x| x.len()).sum(); println!("Iteration complete, total alignments {}", total_alignments); - + Ok(()) -} \ No newline at end of file +} diff --git a/fakeminimap2/src/state.rs b/fakeminimap2/src/state.rs new file mode 100644 index 0000000..64cc1fc --- /dev/null +++ b/fakeminimap2/src/state.rs @@ -0,0 +1,228 @@ +use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use tokio::sync::watch; + +use minimap2::Mapping; + +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +#[derive(Clone)] +pub struct UiState { + pub query_sequences_list: Vec>, + pub selected_query_sequence: Option>, + pub mappings: Vec, + dispatcher_tx: UnboundedSender, +} + +impl UiState { + pub fn new(dispatcher_tx: UnboundedSender) -> Self { + Self { + query_sequences_list: Vec::new(), + selected_query_sequence: None, + mappings: Vec::new(), + dispatcher_tx, + } + } + + pub fn dispatch(&mut self, action: Action) { + match action { + Action::UpdateUiStateSelectQuerySequence((n, query)) => { + self.selected_query_sequence = Some(query); + } + Action::UpdateUiStateSelectMapping(n) => { + self.dispatcher_tx + .send(Action::UpdateUiStateSelectMapping(n)).expect("Unable to send updated UI state"); + } + Action::UpdateUiStateQuerySequencesList(list) => { + self.query_sequences_list = list; + } + _ => unimplemented!("Action not implemented or invalid for UiState"), + } + + // Sending UpdatedUiState action + self.dispatcher_tx + .send(Action::UpdatedUiState).expect("Unable to send updated UI state"); + } +} + +#[derive(Debug, Clone)] +pub struct QuerySequence { + pub id: String, + pub sequence: Vec, +} + +impl QuerySequence { + pub fn new(id: String, sequence: Vec) -> Self { + Self { id, sequence } + } +} + +pub struct QuerySequencesStore { + query_sequences: Vec>, + dispatcher_tx: UnboundedSender, +} + +impl QuerySequencesStore { + pub fn new(dispatcher_tx: UnboundedSender) -> Self { + Self { + query_sequences: Vec::new(), + dispatcher_tx, + } + } + + pub fn dispatch(&mut self, action: Action) { + match action { + Action::AddQuerySequence(query) => { + self.add_query_sequence(query); + } + Action::SetSelectedQuery(n) => { + self.set_current(n); + } + _ => unimplemented!("Action not implemented or invalid for QuerySequencesStore"), + } + } + + /// Add a new query sequence + pub fn add_query_sequence(&mut self, query: QuerySequence) { + self.query_sequences.push(Arc::new(query)); + self.dispatcher_tx + .send(Action::UpdateUiStateQuerySequencesList( + self.query_sequences + .iter() + .map(|q| Arc::clone(&q)) + .collect(), + )) + .expect("Unable to send updated UI state"); + } + + /// Set the currently viewed QuerySequence + pub fn set_current(&self, n: usize) { + let _ = self + .dispatcher_tx + .send(Action::UpdateUiStateSelectQuerySequence(( + n, + Arc::clone(&self.query_sequences[n]), + ))); + } +} + +pub struct MappingResultStore { + state: Arc>>>, + sender: watch::Sender<(String, Vec)>, + highlighted: watch::Sender>, // Tracks highlighted result (Query ID, Index) +} + +impl MappingResultStore { + pub fn new() -> Self { + let (sender, _) = watch::channel((String::new(), Vec::new())); + let (highlighted, _) = watch::channel(None); + Self { + state: Arc::new(Mutex::new(HashMap::new())), + sender, + highlighted, + } + } + + /// Add mapping results and notify subscribers + pub fn add_mapping_results(&self, id: String, mappings: Vec) { + let mut state = self.state.lock().unwrap(); + state.insert(id.clone(), mappings.clone()); + let _ = self.sender.send((id, mappings)); + } + + /// Highlight a specific mapping result + pub fn set_highlighted(&self, id: String, index: usize) { + let _ = self.highlighted.send(Some((id, index))); + } + + /// Subscribe to the highlighted result + pub fn subscribe_highlighted(&self) -> watch::Receiver> { + self.highlighted.subscribe() + } + + /// Get the current state + pub fn get_state(&self) -> HashMap> { + self.state.lock().unwrap().clone() + } +} + +pub enum Action { + // QueryStore + AddQuerySequence(QuerySequence), + SetSelectedQuery(usize), // Will later call: UpdateUiStateSelectQuerySequence + + // Mapping Store (todo) + SetSelectedMapping(usize), // Will later call: UpdateUiStateSelectMapping + + // UI State + UpdateUiStateSelectQuerySequence((usize, Arc)), + UpdateUiStateSelectMapping(usize), + UpdateUiStateQuerySequencesList(Vec>), + + // Pass back to renderer + UpdatedUiState, +} + +enum TargetStore { + QuerySequences, + MappingResults, + UiState, + Ui, +} + +impl Action { + pub fn target_store(&self) -> TargetStore { + match self { + Action::AddQuerySequence(_) | Action::SetSelectedQuery(_) => TargetStore::QuerySequences, + Action::SetSelectedMapping(_) => TargetStore::MappingResults, + Action::UpdateUiStateSelectQuerySequence(_) + | Action::UpdateUiStateSelectMapping(_) + | Action::UpdateUiStateQuerySequencesList(_) => TargetStore::UiState, + Action::UpdatedUiState => TargetStore::Ui, + } + } +} + +pub struct Dispatcher { + sender: UnboundedSender, +} + +impl Dispatcher { + pub fn new() -> (Self, UnboundedReceiver) { + let (sender, receiver) = mpsc::unbounded_channel(); + (Self { sender }, receiver) + } + + /// Dispatch an action + pub fn dispatch(&self, action: Action) { + let _ = self.sender.send(action); + } +} + +pub async fn start_dispatcher( + dispatcher_tx: UnboundedSender, + mut dispatcher_rx: UnboundedReceiver, + ui_tx: UnboundedSender, +) { + + let mut query_store = QuerySequencesStore::new(dispatcher_tx.clone()); + let mut mapping_store = MappingResultStore::new(); + let mut ui_state = UiState::new(dispatcher_tx.clone()); + + // Initial state + ui_tx.send(ui_state.clone()).expect("Unable to send initial state"); + + tokio::spawn(async move { + while let Some(action) = dispatcher_rx.recv().await { + match action.target_store() { + TargetStore::QuerySequences => query_store.dispatch(action), + // TargetStore::MappingResults => mapping_store.dispatch(action), + TargetStore::MappingResults => unimplemented!("Soon"), + TargetStore::UiState => ui_state.dispatch(action), + TargetStore::Ui => { + ui_tx.send(ui_state.clone()); + } + } + } + }); +} \ No newline at end of file diff --git a/fakeminimap2/src/ui.rs b/fakeminimap2/src/ui.rs new file mode 100644 index 0000000..d95ab9e --- /dev/null +++ b/fakeminimap2/src/ui.rs @@ -0,0 +1,39 @@ +use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; + +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use crate::state as state; + +mod app_display; +use app_display::AppDisplay; + +const RENDERING_TICK_RATE: Duration = Duration::from_millis(250); + +pub async fn main_loop( + dispatcher_tx: UnboundedSender, + mut ui_rx: UnboundedReceiver, +) { + // Get the first state + let mut app_display = { + let state = ui_rx.recv().await.unwrap(); + AppDisplay::new(state, dispatcher_tx.clone()) + }; + + app_display.render(); + + + let mut ticker = tokio::time::interval(RENDERING_TICK_RATE); + loop { + tokio::select! { + _ = ticker.tick() => (), + Some(state) = ui_rx.recv() => { + // Update the state + app_display.set_state(state); + } + }; + + app_display.render(); + } + +} \ No newline at end of file diff --git a/fakeminimap2/src/ui/app_display.rs b/fakeminimap2/src/ui/app_display.rs new file mode 100644 index 0000000..b723707 --- /dev/null +++ b/fakeminimap2/src/ui/app_display.rs @@ -0,0 +1,79 @@ +use crossterm::event::{self, Event}; +use ratatui::widgets::{Block, List, ListDirection, ListState}; +use ratatui::prelude::*; +use ratatui::{prelude::CrosstermBackend, text::Text, Frame, Terminal}; +use ratatui::layout::Constraint::{Fill, Length, Min}; + + +use crate::state::{self as state, Dispatcher}; + +pub struct AppDisplay { + terminal: Terminal>, + state: state::UiState, + frame_count: usize, + query_list: ListState, + dispatcher_tx: tokio::sync::mpsc::UnboundedSender, +} + +impl AppDisplay { + pub fn new(state: state::UiState, dispatcher_tx: tokio::sync::mpsc::UnboundedSender) -> Self { + let mut terminal: ratatui::Terminal> = ratatui::init(); + terminal.clear(); + let mut query_list_state = ListState::default(); + Self { terminal, state, frame_count: 0, query_list: query_list_state, dispatcher_tx } + } + + pub fn set_state(&mut self, state: state::UiState) { + self.state = state; + } + + pub fn render(&mut self) { + self.frame_count += 1; + self.terminal.draw(|frame| { + let vertical = Layout::vertical([Length(1), Min(0), Length(1)]); + let [title_area, main_area, status_area] = vertical.areas(frame.area()); + let horizontal = Layout::horizontal([Fill(1), Fill(2)]); + let [left_area, right_area] = horizontal.areas(main_area); + + let mut count = 0; + + // Left area list of query sequence names + if self.state.query_sequences_list.is_empty() { + frame.render_widget(Text::styled("No query sequences", Style::default().fg(Color::Red)), left_area); + } else { + let mut items = vec![]; + for (i, query) in self.state.query_sequences_list.iter().enumerate() { + items.push(query.id.clone()); + } + count = self.state.query_sequences_list.len(); + + if self.query_list.selected().is_none() { + self.query_list.select_first(); + self.dispatcher_tx.send(state::Action::SetSelectedQuery(self.query_list.selected().unwrap())).expect("Unable to send selected query sequence"); + } + + let list = List::new(items) + .block(Block::bordered().title("List")) + .style(Style::new().white()) + .highlight_style(Style::new().italic()) + .highlight_symbol(">>") + .repeat_highlight_symbol(true) + .direction(ListDirection::TopToBottom); + + frame.render_stateful_widget(list, left_area, &mut self.query_list); + } + + + + frame.render_widget(Block::bordered().title("Fakeminimap2"), title_area); + frame.render_widget(Block::bordered().title(format!("Status Bar - {} - {}", count, self.frame_count)), status_area); + frame.render_widget(Block::bordered().title("Query Sequences"), left_area); + frame.render_widget(Block::bordered().title("Mapping"), right_area); + + }).expect("Error rendering"); + } + + pub fn exit(&mut self) { + ratatui::restore(); + } +} \ No newline at end of file From 4befa0b078332a0b936363eeaf5d24fb74786fea Mon Sep 17 00:00:00 2001 From: Joseph Guhlin Date: Tue, 3 Dec 2024 15:11:40 +1300 Subject: [PATCH 2/8] Fakeminimap2 - fun updates --- fakeminimap2/.gitignore | 1 + fakeminimap2/Cargo.lock | 480 ++++++++++++++++-- fakeminimap2/Cargo.toml | 7 +- fakeminimap2/src/channels.rs | 115 +++-- fakeminimap2/src/datatypes.rs | 68 +++ fakeminimap2/src/main.rs | 46 +- fakeminimap2/src/state.rs | 246 ++------- .../src/state/mapping_results_store.rs | 63 +++ .../src/state/query_sequences_store.rs | 87 ++++ fakeminimap2/src/state/ui_state.rs | 71 +++ fakeminimap2/src/ui.rs | 107 +++- fakeminimap2/src/ui/app_display.rs | 398 +++++++++++++-- 12 files changed, 1340 insertions(+), 349 deletions(-) create mode 100644 fakeminimap2/.gitignore create mode 100644 fakeminimap2/src/datatypes.rs create mode 100644 fakeminimap2/src/state/mapping_results_store.rs create mode 100644 fakeminimap2/src/state/query_sequences_store.rs create mode 100644 fakeminimap2/src/state/ui_state.rs diff --git a/fakeminimap2/.gitignore b/fakeminimap2/.gitignore new file mode 100644 index 0000000..397b4a7 --- /dev/null +++ b/fakeminimap2/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/fakeminimap2/Cargo.lock b/fakeminimap2/Cargo.lock index 113d9a9..188fb9c 100644 --- a/fakeminimap2/Cargo.lock +++ b/fakeminimap2/Cargo.lock @@ -32,6 +32,21 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.18" @@ -81,6 +96,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.4.0" @@ -102,6 +123,34 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "bindgen" +version = "0.63.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" @@ -130,7 +179,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.89", ] [[package]] @@ -142,6 +191,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytecount" version = "0.6.8" @@ -201,12 +256,44 @@ dependencies = [ "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.21" @@ -238,7 +325,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -273,6 +360,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crc32fast" version = "1.4.2" @@ -344,8 +437,9 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags", + "bitflags 2.6.0", "crossterm_winapi", + "futures-core", "mio", "parking_lot", "rustix", @@ -384,7 +478,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.89", ] [[package]] @@ -395,7 +489,21 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.89", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] @@ -411,26 +519,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "env_filter" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.5" +name = "encoding_rs" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", + "cfg-if", ] [[package]] @@ -456,15 +550,18 @@ dependencies = [ "clap", "crossbeam", "crossterm", - "env_logger", + "dashmap", + "flexi_logger", "humantime", "log", "minimap2", "needletail", + "num-format", "ratatui", "rayon", "tachyonfx", "tokio", + "tokio-stream", ] [[package]] @@ -477,6 +574,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flexi_logger" +version = "0.29.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26948e37cfcb1f2c2cd38e0602d3a8ab6b9472c0c6eff4516fc8def9a3124d7" +dependencies = [ + "chrono", + "log", + "nu-ansi-term", + "regex", + "thiserror", +] + [[package]] name = "fnv" version = "1.0.7" @@ -489,12 +599,36 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.2" @@ -518,12 +652,44 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -547,7 +713,7 @@ dependencies = [ "pretty_assertions", "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -580,12 +746,44 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.166" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + [[package]] name = "liblzma" version = "0.3.5" @@ -646,7 +844,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown", + "hashbrown 0.15.2", ] [[package]] @@ -655,9 +853,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "minimap2" -version = "0.1.21+minimap2.2.28" +version = "0.1.22+minimap2.2.28" dependencies = [ "libc", "minimap2-sys", @@ -709,6 +913,60 @@ dependencies = [ "memchr", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "cfg-if", + "encoding_rs", + "itoa", + "lazy_static", + "libc", + "num-format-windows", + "widestring", + "winapi", +] + +[[package]] +name = "num-format-windows" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1e07f67225c1eb911d16c2f72492669c1fb08212dbfaa1f6cfbeb119152cfa" +dependencies = [ + "bindgen", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.36.5" @@ -718,6 +976,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "parking_lot" version = "0.12.3" @@ -747,6 +1011,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "pin-project-lite" version = "0.2.15" @@ -776,7 +1046,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.89", ] [[package]] @@ -803,7 +1073,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags", + "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", @@ -844,7 +1114,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -882,13 +1152,19 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ - "bitflags", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1002,7 +1278,18 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.89", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -1028,6 +1315,26 @@ dependencies = [ "simple-easing", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "tokio" version = "1.41.1" @@ -1054,7 +1361,32 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", ] [[package]] @@ -1110,6 +1442,79 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.89", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -1132,6 +1537,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/fakeminimap2/Cargo.toml b/fakeminimap2/Cargo.toml index c52cadd..429e538 100644 --- a/fakeminimap2/Cargo.toml +++ b/fakeminimap2/Cargo.toml @@ -11,13 +11,16 @@ crossbeam = "0.8.4" needletail = "0.6" humantime = "2.1.0" log = "0.4.22" -env_logger = "0.11.5" +flexi_logger = "0.29" clap = { version = "4.5.21", features = ["derive"] } rayon = "1.10.0" ratatui = "0.29.0" tokio = { version = "1", features = ["full"] } -crossterm = "0.28.1" +crossterm = { version = "0.28.1", features = ["event-stream"] } tachyonfx = "0.9.3" +tokio-stream = { version = "0.1.16", features = ["sync"] } +num-format = { version = "0.4.4", features = ["with-system-locale"] } +dashmap = "6.1.0" [profile.release] opt-level = 3 diff --git a/fakeminimap2/src/channels.rs b/fakeminimap2/src/channels.rs index bb802e0..55add1e 100644 --- a/fakeminimap2/src/channels.rs +++ b/fakeminimap2/src/channels.rs @@ -6,7 +6,6 @@ use std::{error::Error, path::Path, sync::Arc, time::Duration}; use crate::state::QuerySequence; - /// We use a worker queue to pass around work between threads. /// We do it this way to be generic over the type. enum WorkQueue { @@ -32,18 +31,31 @@ pub(crate) fn map_with_channels( // UI Stuff dispatcher_tx: tokio::sync::mpsc::UnboundedSender, ) -> Result<(), Box> { + dispatcher_tx + .send(crate::state::Action::SetStatus( + "Indexing Sequence".to_string(), + )) + .expect("Unable to send status update"); + // Aligner gets created using the build pattern. // Once .with_index is called, the aligner is set to "Built" and can no longer be changed. let aligner = Aligner::builder() .map_ont() .with_cigar() + .with_index_threads(threads) .with_index(target_file, None) .expect("Unable to build index"); + dispatcher_tx + .send(crate::state::Action::SetStatus( + "Indexing Complete".to_string(), + )) + .expect("Unable to send status update"); + log::info!("Made aligner"); // Create a queue for work and for results - let work_queue = Arc::new(ArrayQueue::>::new(32)); // Artificially low, but the best depends on tuning - let results_queue = Arc::new(ArrayQueue::>::new(32)); + let work_queue = Arc::new(ArrayQueue::>::new(1024)); + let results_queue = Arc::new(ArrayQueue::>::new(1024)); // I use a shutdown flag let shutdown = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); @@ -55,6 +67,7 @@ pub(crate) fn map_with_channels( // Spin up the threads log::info!("Spawn threads"); + log::trace!("Spawning {} threads", threads); for _ in 0..threads { // Clone everything we will need... let work_queue = Arc::clone(&work_queue); @@ -68,68 +81,106 @@ pub(crate) fn map_with_channels( jh.push(handle); } - // Now that the threads are running, read the input file and push the work to the queue - let mut reader: Box = - parse_fastx_file(query_file).unwrap_or_else(|_| panic!("Can't find query FASTA file")); + // Let's split this into another thread - // I just do this in the main thread, but you can split threads - let backoff = crossbeam::utils::Backoff::new(); - while let Some(Ok(record)) = reader.next() { - let mut work = WorkQueue::Work((record.id().to_vec(), record.seq().to_vec())); - while let Err(work_packet) = work_queue.push(work) { - work = work_packet; // Simple way to maintain ownership - // If we have an error, it's 99% because the queue is full - backoff.snooze(); - } + { + let work_queue = Arc::clone(&work_queue); + let shutdown = Arc::clone(&shutdown); + let dispatcher_tx = dispatcher_tx.clone(); + let query_file = query_file.as_ref().to_path_buf(); + + let handle = std::thread::spawn(move || { + // Now that the threads are running, read the input file and push the work to the queue + let mut reader: Box = parse_fastx_file(query_file) + .unwrap_or_else(|_| panic!("Can't find query FASTA file")); + + dispatcher_tx + .send(crate::state::Action::SetStatus( + "Mapping Sequences".to_string(), + )) + .expect("Unable to send status update"); + + // I just do this in the main thread, but you can split threads + let backoff = crossbeam::utils::Backoff::new(); + while let Some(Ok(record)) = reader.next() { + let mut work = WorkQueue::Work((record.id().to_vec(), record.seq().to_vec())); + while let Err(work_packet) = work_queue.push(work) { + work = work_packet; // Simple way to maintain ownership + // If we have an error, it's 99% because the queue is full + backoff.snooze(); + } - // Oh and add it to the UI (this should be first, but trying to keep multithreading and UI separate) - let _ = dispatcher_tx.send(crate::state::Action::AddQuerySequence( - QuerySequence::new(std::str::from_utf8(record.id()).unwrap().to_string(), record.seq().to_vec()), - )); - println!("Sent query sequence"); - } + // Oh and add it to the UI (this should be first, but trying to keep multithreading and UI separate) + let _ = + dispatcher_tx.send(crate::state::Action::AddQuerySequence(QuerySequence::new( + std::str::from_utf8(record.id()).unwrap().to_string(), + record.seq().to_vec(), + ))); + } + + dispatcher_tx + .send(crate::state::Action::SetStatus( + "All Sequences Submitted".to_string(), + )) + .expect("Unable to send status update"); - // Set the shutdown flag - shutdown.store(true, std::sync::atomic::Ordering::Relaxed); + // Set the shutdown flag + shutdown.store(true, std::sync::atomic::Ordering::Relaxed); + }); + + jh.push(handle); + } let mut num_alignments = 0; let backoff = crossbeam::utils::Backoff::new(); loop { match results_queue.pop() { + // This is where we processs mapping results as they come in... Some(WorkQueue::Result((record, alignments))) => { + log::trace!("Found {} alignments", alignments.len()); num_alignments += alignments.len(); log::info!( "Got {} alignments for {}", alignments.len(), std::str::from_utf8(&record.0).unwrap() ); + + // Add the mappings to the UI + let _ = dispatcher_tx.send(crate::state::Action::AddMappings(( + std::str::from_utf8(&record.0).unwrap().to_string(), + alignments, + ))); } Some(_) => unimplemented!("Unexpected result type"), None => { - log::trace!("Awaiting results"); backoff.snooze(); - // If all join handles are 'finished' we can break + // If all join handles are finished, we can break if jh.iter().all(|h| h.is_finished()) { + log::trace!("All threads finished - Breaking from Receiving Results Loop"); break; } if backoff.is_completed() { backoff.reset(); - std::thread::sleep(Duration::from_millis(1)); + std::thread::sleep(Duration::from_millis(3)); } } } } - println!("Iteration complete, total alignments {}", num_alignments); + dispatcher_tx + .send(crate::state::Action::SetStatus( + "Mapping Complete".to_string(), + )) + .expect("Unable to send status update"); - // Join all threads + log::info!("Iteration complete, total alignments {}", num_alignments); + + // Join all the threads + log::trace!("Joining all threads"); for handle in jh { - match handle.join() { - Ok(_) => log::trace!("Thread finished"), - Err(e) => log::error!("Thread panicked: {:?}", e), - } + handle.join().expect("Unable to join thread"); } Ok(()) diff --git a/fakeminimap2/src/datatypes.rs b/fakeminimap2/src/datatypes.rs new file mode 100644 index 0000000..f42ddf0 --- /dev/null +++ b/fakeminimap2/src/datatypes.rs @@ -0,0 +1,68 @@ +use crossterm::event::KeyEvent; +use minimap2::Mapping; +use tokio::sync::Mutex; + +use std::sync::Arc; + +#[derive(Debug, Clone, PartialEq)] +pub struct QuerySequence { + pub id: String, + pub sequence: Vec, +} + +impl QuerySequence { + pub fn new(id: String, sequence: Vec) -> Self { + Self { id, sequence } + } +} +pub enum Action { + // QueryStore + AddQuerySequence(QuerySequence), + SetSelectedQuery(usize), // Will later call: UpdateUiStateSelectQuerySequence + + // Mapping Store (todo) + SetSelectedMappings(String), // Will later call: UpdateUiStateSelectMappings + AddMappings((String, Vec)), + + // UI State + UpdateUiStateSelectQuerySequence((usize, Arc)), + UpdateUiStateSelectMappings(Arc>>), + UpdateUiStateSelectMapping(usize), + UpdateUiStateQuerySequencesList(Vec>), + SetStatus(String), + + // Pass back to renderer + UpdatedUiState, + + // Key Press + KeyPress(KeyEvent), +} +pub enum TargetStore { + QuerySequences, + MappingResults, + UiState, + Ui, + Dispatcher, +} + +impl Action { + pub fn target_store(&self) -> TargetStore { + match self { + Action::AddQuerySequence(_) | Action::SetSelectedQuery(_) => { + TargetStore::QuerySequences + } + + Action::SetSelectedMappings(_) | Action::AddMappings(_) => TargetStore::MappingResults, + + Action::UpdateUiStateSelectQuerySequence(_) + | Action::UpdateUiStateSelectMappings(_) + | Action::UpdateUiStateSelectMapping(_) + | Action::SetStatus(_) + | Action::UpdateUiStateQuerySequencesList(_) => TargetStore::UiState, + + Action::UpdatedUiState => TargetStore::Ui, + + Action::KeyPress(_) => TargetStore::Dispatcher, + } + } +} diff --git a/fakeminimap2/src/main.rs b/fakeminimap2/src/main.rs index 9ed0671..c5992b3 100644 --- a/fakeminimap2/src/main.rs +++ b/fakeminimap2/src/main.rs @@ -9,33 +9,40 @@ mod cli; mod channels; // I prefer using channels over rayon, but rayon is simpler to use mod rayon; -// UI Stuff -mod ui; -use tokio::sync::mpsc; +use flexi_logger::{FileSpec, Logger, WriteMode}; // Ignore the tokio stuff, it's just for visualization and interaction! #[tokio::main] async fn main() { - env_logger::init(); + flexi_logger::Logger::try_with_env_or_str("info") + .unwrap() + .log_to_file(FileSpec::default()) + .write_mode(WriteMode::BufferAndFlush) + .start() + .expect("Unable to start logger"); // Parse command line arguments let args = cli::parse_args(); - + // UI Stuff let (dispatcher_tx, dispatcher_rx) = mpsc::unbounded_channel::(); - let (ui_tx, ui_rx) = mpsc::unbounded_channel::(); + let (ui_tx, ui_rx) = tokio::sync::watch::channel(None); { let dispatcher_tx = dispatcher_tx.clone(); - let handle = std::thread::spawn(move || { - match args.method.unwrap_or_default() { - cli::Method::Channels => { - channels::map_with_channels(args.target, args.query, args.threads, dispatcher_tx.clone()) - .expect("Error mapping with channels"); - } - cli::Method::Rayon => { - rayon::map(args.target, args.query, args.threads, dispatcher_tx.clone()).expect("Error mapping with rayon"); - } + let handle = std::thread::spawn(move || match args.method.unwrap_or_default() { + cli::Method::Channels => { + channels::map_with_channels( + args.target, + args.query, + args.threads, + dispatcher_tx.clone(), + ) + .expect("Error mapping with channels"); + } + cli::Method::Rayon => { + rayon::map(args.target, args.query, args.threads, dispatcher_tx.clone()) + .expect("Error mapping with rayon"); } }); } @@ -47,4 +54,13 @@ async fn main() { ); } +// Trying to keep UI separated from mapping for easier code understanding +// UI Stuff +// UI Inspo: https://github.com/Yengas/rust-chat-server/tree/main/tui +mod datatypes; mod state; +mod ui; + +pub use datatypes::*; + +use tokio::sync::mpsc; diff --git a/fakeminimap2/src/state.rs b/fakeminimap2/src/state.rs index 64cc1fc..ceebce1 100644 --- a/fakeminimap2/src/state.rs +++ b/fakeminimap2/src/state.rs @@ -1,3 +1,4 @@ +use crossterm::event::KeyCode; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::sync::watch; @@ -6,223 +7,66 @@ use minimap2::Mapping; use std::collections::HashMap; use std::sync::{Arc, Mutex}; -#[derive(Clone)] -pub struct UiState { - pub query_sequences_list: Vec>, - pub selected_query_sequence: Option>, - pub mappings: Vec, - dispatcher_tx: UnboundedSender, -} - -impl UiState { - pub fn new(dispatcher_tx: UnboundedSender) -> Self { - Self { - query_sequences_list: Vec::new(), - selected_query_sequence: None, - mappings: Vec::new(), - dispatcher_tx, - } - } - - pub fn dispatch(&mut self, action: Action) { - match action { - Action::UpdateUiStateSelectQuerySequence((n, query)) => { - self.selected_query_sequence = Some(query); - } - Action::UpdateUiStateSelectMapping(n) => { - self.dispatcher_tx - .send(Action::UpdateUiStateSelectMapping(n)).expect("Unable to send updated UI state"); - } - Action::UpdateUiStateQuerySequencesList(list) => { - self.query_sequences_list = list; - } - _ => unimplemented!("Action not implemented or invalid for UiState"), - } - - // Sending UpdatedUiState action - self.dispatcher_tx - .send(Action::UpdatedUiState).expect("Unable to send updated UI state"); - } -} - -#[derive(Debug, Clone)] -pub struct QuerySequence { - pub id: String, - pub sequence: Vec, -} - -impl QuerySequence { - pub fn new(id: String, sequence: Vec) -> Self { - Self { id, sequence } - } -} - -pub struct QuerySequencesStore { - query_sequences: Vec>, - dispatcher_tx: UnboundedSender, -} - -impl QuerySequencesStore { - pub fn new(dispatcher_tx: UnboundedSender) -> Self { - Self { - query_sequences: Vec::new(), - dispatcher_tx, - } - } - - pub fn dispatch(&mut self, action: Action) { - match action { - Action::AddQuerySequence(query) => { - self.add_query_sequence(query); - } - Action::SetSelectedQuery(n) => { - self.set_current(n); - } - _ => unimplemented!("Action not implemented or invalid for QuerySequencesStore"), - } - } - - /// Add a new query sequence - pub fn add_query_sequence(&mut self, query: QuerySequence) { - self.query_sequences.push(Arc::new(query)); - self.dispatcher_tx - .send(Action::UpdateUiStateQuerySequencesList( - self.query_sequences - .iter() - .map(|q| Arc::clone(&q)) - .collect(), - )) - .expect("Unable to send updated UI state"); - } - - /// Set the currently viewed QuerySequence - pub fn set_current(&self, n: usize) { - let _ = self - .dispatcher_tx - .send(Action::UpdateUiStateSelectQuerySequence(( - n, - Arc::clone(&self.query_sequences[n]), - ))); - } -} +pub mod mapping_results_store; +pub mod query_sequences_store; +pub mod ui_state; -pub struct MappingResultStore { - state: Arc>>>, - sender: watch::Sender<(String, Vec)>, - highlighted: watch::Sender>, // Tracks highlighted result (Query ID, Index) -} - -impl MappingResultStore { - pub fn new() -> Self { - let (sender, _) = watch::channel((String::new(), Vec::new())); - let (highlighted, _) = watch::channel(None); - Self { - state: Arc::new(Mutex::new(HashMap::new())), - sender, - highlighted, - } - } - - /// Add mapping results and notify subscribers - pub fn add_mapping_results(&self, id: String, mappings: Vec) { - let mut state = self.state.lock().unwrap(); - state.insert(id.clone(), mappings.clone()); - let _ = self.sender.send((id, mappings)); - } - - /// Highlight a specific mapping result - pub fn set_highlighted(&self, id: String, index: usize) { - let _ = self.highlighted.send(Some((id, index))); - } - - /// Subscribe to the highlighted result - pub fn subscribe_highlighted(&self) -> watch::Receiver> { - self.highlighted.subscribe() - } - - /// Get the current state - pub fn get_state(&self) -> HashMap> { - self.state.lock().unwrap().clone() - } -} - -pub enum Action { - // QueryStore - AddQuerySequence(QuerySequence), - SetSelectedQuery(usize), // Will later call: UpdateUiStateSelectQuerySequence - - // Mapping Store (todo) - SetSelectedMapping(usize), // Will later call: UpdateUiStateSelectMapping - - // UI State - UpdateUiStateSelectQuerySequence((usize, Arc)), - UpdateUiStateSelectMapping(usize), - UpdateUiStateQuerySequencesList(Vec>), - - // Pass back to renderer - UpdatedUiState, -} - -enum TargetStore { - QuerySequences, - MappingResults, - UiState, - Ui, -} - -impl Action { - pub fn target_store(&self) -> TargetStore { - match self { - Action::AddQuerySequence(_) | Action::SetSelectedQuery(_) => TargetStore::QuerySequences, - Action::SetSelectedMapping(_) => TargetStore::MappingResults, - Action::UpdateUiStateSelectQuerySequence(_) - | Action::UpdateUiStateSelectMapping(_) - | Action::UpdateUiStateQuerySequencesList(_) => TargetStore::UiState, - Action::UpdatedUiState => TargetStore::Ui, - } - } -} - -pub struct Dispatcher { - sender: UnboundedSender, -} - -impl Dispatcher { - pub fn new() -> (Self, UnboundedReceiver) { - let (sender, receiver) = mpsc::unbounded_channel(); - (Self { sender }, receiver) - } - - /// Dispatch an action - pub fn dispatch(&self, action: Action) { - let _ = self.sender.send(action); - } -} +pub use crate::datatypes::*; +pub use mapping_results_store::MappingResultStore; +pub use query_sequences_store::QuerySequencesStore; +pub use ui_state::{SelectedPanel, UiState}; pub async fn start_dispatcher( dispatcher_tx: UnboundedSender, mut dispatcher_rx: UnboundedReceiver, - ui_tx: UnboundedSender, + ui_tx: watch::Sender>, ) { - let mut query_store = QuerySequencesStore::new(dispatcher_tx.clone()); - let mut mapping_store = MappingResultStore::new(); + let mut mapping_store = Arc::new(MappingResultStore::new(dispatcher_tx.clone())); let mut ui_state = UiState::new(dispatcher_tx.clone()); // Initial state - ui_tx.send(ui_state.clone()).expect("Unable to send initial state"); + ui_tx + .send(Some(ui_state.clone())) + .expect("Unable to send initial state"); tokio::spawn(async move { while let Some(action) = dispatcher_rx.recv().await { match action.target_store() { - TargetStore::QuerySequences => query_store.dispatch(action), - // TargetStore::MappingResults => mapping_store.dispatch(action), - TargetStore::MappingResults => unimplemented!("Soon"), - TargetStore::UiState => ui_state.dispatch(action), + TargetStore::QuerySequences => query_store.dispatch(action).await, + TargetStore::MappingResults => { + let mapping_store = Arc::clone(&mapping_store); + mapping_store.dispatch(action).await + } + TargetStore::UiState => ui_state.dispatch(action).await, TargetStore::Ui => { - ui_tx.send(ui_state.clone()); + ui_tx + .send(Some(ui_state.clone())) + .expect("Unable to send UI state"); + } + TargetStore::Dispatcher => { + // Means we have to make the decision ourselves + match action { + Action::KeyPress(key) => { + match key.code { + KeyCode::Char('q') => { + let mut ui_state = ui_state.clone(); + ui_state.shutdown = true; + ui_tx.send(Some(ui_state)).expect("Unable to send shutdown"); + } + _ => { + // What's the selected panel? + match ui_state.selected_panel { + SelectedPanel::QuerySequences => query_store.keypress(key), + SelectedPanel::Mappings => mapping_store.keypress(key), + } + } + } + } + _ => unimplemented!("Action not implemented or invalid for Dispatcher"), + } } } } }); -} \ No newline at end of file +} diff --git a/fakeminimap2/src/state/mapping_results_store.rs b/fakeminimap2/src/state/mapping_results_store.rs new file mode 100644 index 0000000..a47ea70 --- /dev/null +++ b/fakeminimap2/src/state/mapping_results_store.rs @@ -0,0 +1,63 @@ +pub use crate::datatypes::*; + +use crossterm::event::KeyEvent; +use dashmap::DashMap; +use minimap2::Mapping; +use tokio::sync::{mpsc::UnboundedSender, Mutex}; + +use std::sync::Arc; + +pub struct MappingResultStore { + dispatcher_tx: UnboundedSender, + mappings: DashMap>>>, +} + +impl MappingResultStore { + pub fn new(dispatcher_tx: UnboundedSender) -> Self { + Self { + dispatcher_tx, + mappings: DashMap::new(), + } + } + + pub async fn dispatch(self: Arc, action: Action) { + match action { + Action::SetSelectedMappings(id) => { + if self.mappings.get(&id).is_none() { + self.mappings + .insert(id.clone(), Arc::new(Mutex::new(Vec::new()))); + } + + let selected_mappings = Arc::clone(&self.mappings.get(&id).unwrap()); + self.dispatcher_tx + .send(Action::UpdateUiStateSelectMappings(selected_mappings)) + .expect("Unable to send updated UI state"); + } + + Action::AddMappings((id, mappings)) => { + let s = Arc::clone(&self); + + tokio::spawn(async move { s.add_mappings(id, mappings).await }); + } + _ => unimplemented!("Action not implemented or invalid for MappingResultStore"), + } + } + + pub async fn add_mappings(self: Arc, id: String, mapping: Vec) { + log::debug!("Adding mappings for id: {} - {} found", id, mapping.len()); + // If the entry already exists, update the mappings + if self.mappings.get(&id).is_some() { + log::trace!("Entry exists - Updating mappings for id: {}", id); + let selected_mappings = Arc::clone(&self.mappings.get(&id).unwrap()); + tokio::spawn(async move { + let mut selected_mappings = selected_mappings.lock().await; + selected_mappings.extend(mapping); + }); + } else { + self.mappings + .insert(id.clone(), Arc::new(Mutex::new(mapping))); + } + } + + pub fn keypress(&self, key: KeyEvent) {} +} diff --git a/fakeminimap2/src/state/query_sequences_store.rs b/fakeminimap2/src/state/query_sequences_store.rs new file mode 100644 index 0000000..f028cf3 --- /dev/null +++ b/fakeminimap2/src/state/query_sequences_store.rs @@ -0,0 +1,87 @@ +pub use crate::datatypes::*; + +use crossterm::event::KeyEvent; +use std::sync::Arc; +use tokio::sync::mpsc::UnboundedSender; + +pub struct QuerySequencesStore { + query_sequences: Vec>, + dispatcher_tx: UnboundedSender, + current: usize, +} + +impl QuerySequencesStore { + pub fn new(dispatcher_tx: UnboundedSender) -> Self { + Self { + query_sequences: Vec::new(), + dispatcher_tx, + current: 0, + } + } + + pub async fn dispatch(&mut self, action: Action) { + match action { + Action::AddQuerySequence(query) => { + self.add_query_sequence(query); + } + Action::SetSelectedQuery(n) => { + self.set_current(n); + // Set selected mappings + self.dispatcher_tx + .send(Action::SetSelectedMappings( + self.query_sequences[n].id.clone(), + )) + .expect("Unable to send selected mappings"); + } + _ => unimplemented!("Action not implemented or invalid for QuerySequencesStore"), + } + } + + /// Add a new query sequence + pub fn add_query_sequence(&mut self, query: QuerySequence) { + self.query_sequences.push(Arc::new(query)); + self.dispatcher_tx + .send(Action::UpdateUiStateQuerySequencesList( + self.query_sequences + .iter() + .map(|q| Arc::clone(&q)) + .collect(), + )) + .expect("Unable to send updated UI state"); + } + + /// Set the currently viewed QuerySequence + pub fn set_current(&mut self, n: usize) { + self.current = n; + let _ = self + .dispatcher_tx + .send(Action::UpdateUiStateSelectQuerySequence(( + n, + Arc::clone(&self.query_sequences[n]), + ))); + } + + pub fn keypress(&self, key: KeyEvent) { + match key.code { + // Up arrow + crossterm::event::KeyCode::Up => { + log::trace!("Up arrow pressed"); + if self.query_sequences.len() > 0 && self.current > 0 { + self.dispatcher_tx + .send(Action::SetSelectedQuery(self.current - 1)) + .expect("Unable to send selected query"); + } + } + // Down arrow + crossterm::event::KeyCode::Down => { + log::trace!("Down arrow pressed"); + if self.query_sequences.len() > 0 && self.current < self.query_sequences.len() - 1 { + self.dispatcher_tx + .send(Action::SetSelectedQuery(self.current + 1)) + .expect("Unable to send selected query"); + } + } + _ => {} + } + } +} diff --git a/fakeminimap2/src/state/ui_state.rs b/fakeminimap2/src/state/ui_state.rs new file mode 100644 index 0000000..4faf2d3 --- /dev/null +++ b/fakeminimap2/src/state/ui_state.rs @@ -0,0 +1,71 @@ +pub use crate::datatypes::*; + +use minimap2::Mapping; +use tokio::sync::{mpsc::UnboundedSender, Mutex}; + +use std::sync::Arc; + +#[derive(Default, Clone, PartialEq, Eq)] +pub enum SelectedPanel { + #[default] + QuerySequences, + Mappings, +} + +#[derive(Clone)] +pub struct UiState { + pub query_sequences_list: Vec>, + pub selected_query_sequence: Option>, + pub mappings: Option>>>, + pub selected_panel: SelectedPanel, + pub shutdown: bool, + pub status: String, + + dispatcher_tx: UnboundedSender, +} + +impl UiState { + pub fn new(dispatcher_tx: UnboundedSender) -> Self { + Self { + query_sequences_list: Vec::new(), + selected_query_sequence: None, + mappings: None, + shutdown: false, + selected_panel: SelectedPanel::default(), + status: "Starting up!".to_string(), + dispatcher_tx, + } + } + + pub async fn dispatch(&mut self, action: Action) { + match action { + Action::UpdateUiStateSelectQuerySequence((n, query)) => { + self.selected_query_sequence = Some(query); + } + Action::UpdateUiStateSelectMapping(n) => { + self.dispatcher_tx + .send(Action::UpdateUiStateSelectMapping(n)) + .expect("Unable to send updated UI state"); + } + Action::UpdateUiStateQuerySequencesList(list) => { + self.query_sequences_list = list; + } + Action::UpdateUiStateSelectMappings(mappings) => { + self.mappings = Some(mappings); + } + Action::SetStatus(status) => { + self.status = status; + // Send an updated UI state + self.dispatcher_tx + .send(Action::UpdatedUiState) + .expect("Unable to send updated UI state"); + } + _ => unimplemented!("Action not implemented or invalid for UiState"), + } + + // Sending UpdatedUiState action + self.dispatcher_tx + .send(Action::UpdatedUiState) + .expect("Unable to send updated UI state"); + } +} diff --git a/fakeminimap2/src/ui.rs b/fakeminimap2/src/ui.rs index d95ab9e..de0ec2a 100644 --- a/fakeminimap2/src/ui.rs +++ b/fakeminimap2/src/ui.rs @@ -1,39 +1,116 @@ -use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use crossterm::{ + event::{ + DisableMouseCapture, EnableMouseCapture, Event, EventStream, MouseEvent, MouseEventKind, + }, + execute, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, +}; +use ratatui::prelude::*; +use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; +use tokio_stream::{wrappers::WatchStream, StreamExt}; -use std::sync::{Arc, Mutex}; use std::time::Duration; +use std::time::Instant; -use crate::state as state; +use crate::state; mod app_display; use app_display::AppDisplay; -const RENDERING_TICK_RATE: Duration = Duration::from_millis(250); +const RENDERING_TICK_RATE: Duration = Duration::from_millis(200); pub async fn main_loop( dispatcher_tx: UnboundedSender, - mut ui_rx: UnboundedReceiver, + mut ui_rx: tokio::sync::watch::Receiver>, ) { + ui_rx + .wait_for(|val| val.is_some()) + .await + .expect("Unable to get initial UI state"); // Get the first state let mut app_display = { - let state = ui_rx.recv().await.unwrap(); - AppDisplay::new(state, dispatcher_tx.clone()) + let state = ui_rx.borrow(); + AppDisplay::new(state.as_ref().unwrap().clone(), dispatcher_tx.clone()) }; - app_display.render(); - - + let mut terminal = setup_terminal().expect("Unable to setup terminal"); let mut ticker = tokio::time::interval(RENDERING_TICK_RATE); + let mut crossterm_events = EventStream::new(); + + app_display.prepare(&mut terminal); + app_display.render(&mut terminal).await; + + let mut last_frame_instant = Instant::now(); + + let mut ui_rx = WatchStream::new(ui_rx); + loop { + app_display.last_tick = last_frame_instant.elapsed().into(); + last_frame_instant = Instant::now(); + tokio::select! { _ = ticker.tick() => (), - Some(state) = ui_rx.recv() => { + Some(state) = ui_rx.next() => { // Update the state - app_display.set_state(state); - } + app_display.set_state(state.unwrap()); + }, + evt = crossterm_events.next() => match evt { + Some(Ok(Event::Key(key))) => { + dispatcher_tx.send(state::Action::KeyPress(key)).expect("Unable to send key press event"); + }, + Some(Ok(Event::Mouse(evt))) => { + let col = evt.column; + let row = evt.row; + + match evt.kind { + MouseEventKind::Down(_) => { + app_display.handle_click(col, row); + }, + MouseEventKind::Up(_) => { + // + }, + MouseEventKind::Moved => { + } + _ => (), + } + // dispatcher_tx.send(state::Action::Mouse(mouse)).expect("Unable to send mouse event"); + }, + None => break, // Ok(Interrupted::UserInt), + _ => (), + }, }; - app_display.render(); + if app_display.should_quit() { + break; + } + + app_display.render(&mut terminal).await; } -} \ No newline at end of file + restore_terminal(&mut terminal).expect("Unable to restore terminal"); +} + +fn setup_terminal() -> Result>, &'static str> { + let mut stdout = std::io::stdout(); + + enable_raw_mode().expect("Unable to enable raw mode"); + + execute!(stdout, EnterAlternateScreen, EnableMouseCapture).expect("Unable to setup terminal"); + + Ok(Terminal::new(CrosstermBackend::new(stdout)).expect("Unable to create terminal")) +} + +fn restore_terminal( + terminal: &mut Terminal>, +) -> Result<(), &'static str> { + disable_raw_mode().expect("Unable to disable raw mode"); + + execute!( + terminal.backend_mut(), + LeaveAlternateScreen, + DisableMouseCapture + ) + .expect("Unable to restore terminal"); + + Ok(terminal.show_cursor().expect("Unable to show cursor")) +} diff --git a/fakeminimap2/src/ui/app_display.rs b/fakeminimap2/src/ui/app_display.rs index b723707..25b40cb 100644 --- a/fakeminimap2/src/ui/app_display.rs +++ b/fakeminimap2/src/ui/app_display.rs @@ -1,79 +1,375 @@ use crossterm::event::{self, Event}; -use ratatui::widgets::{Block, List, ListDirection, ListState}; +use layout::Flex; +use num_format::SystemLocale; +use num_format::{Locale, ToFormattedString}; +use ratatui::layout::Constraint::{Fill, Length, Min}; use ratatui::prelude::*; +use ratatui::symbols; +use ratatui::widgets::{Axis, Chart, Dataset, GraphType}; +use ratatui::widgets::{Block, Cell, Clear, List, ListDirection, ListState, Paragraph, Row, Table}; use ratatui::{prelude::CrosstermBackend, text::Text, Frame, Terminal}; -use ratatui::layout::Constraint::{Fill, Length, Min}; +use tachyonfx::fx::parallel; +use tachyonfx::Shader; +use tachyonfx::{fx, fx::Direction, Duration, Effect, EffectRenderer, Interpolation::*}; +use crate::state::{self, ui_state}; -use crate::state::{self as state, Dispatcher}; +const COLORS: [Color; 6] = [ + // Pink, Yellow, Brighter Pink, Brighter Yellow, Darker Pink, Darker Yellow + Color::Rgb(255, 62, 181), + Color::Rgb(255, 233, 0), + Color::Rgb(201, 0, 124), + Color::Rgb(255, 243, 112), + Color::Rgb(255, 112, 200), + Color::Rgb(156, 142, 0) + ]; pub struct AppDisplay { - terminal: Terminal>, state: state::UiState, frame_count: usize, query_list: ListState, dispatcher_tx: tokio::sync::mpsc::UnboundedSender, + query_list_rect: Rect, + + left_area_effect: Option, + mappings_list_effect: Option, + plot_area_effect: Option, + + pub last_tick: Duration, + pub plot_data: Vec>, } impl AppDisplay { - pub fn new(state: state::UiState, dispatcher_tx: tokio::sync::mpsc::UnboundedSender) -> Self { - let mut terminal: ratatui::Terminal> = ratatui::init(); - terminal.clear(); - let mut query_list_state = ListState::default(); - Self { terminal, state, frame_count: 0, query_list: query_list_state, dispatcher_tx } + pub fn new( + state: state::UiState, + dispatcher_tx: tokio::sync::mpsc::UnboundedSender, + ) -> Self { + let query_list_state = ListState::default(); + Self { + state, + frame_count: 0, + query_list: query_list_state, + dispatcher_tx, + query_list_rect: Rect::default(), + left_area_effect: None, + mappings_list_effect: None, + plot_area_effect: None, + last_tick: Duration::ZERO, + plot_data: Vec::new(), + } + } + + pub fn should_quit(&self) -> bool { + self.state.shutdown } pub fn set_state(&mut self, state: state::UiState) { + if self.state.selected_query_sequence != state.selected_query_sequence { + self.mappings_list_effect = None; + + self.plot_area_effect = None; + } + self.state = state; } - pub fn render(&mut self) { + pub fn handle_click(&mut self, col: u16, row: u16) { + if self.query_list_rect.contains(Position { x: col, y: row }) { + // Calculate offset from the top of the list + let offset = row.saturating_sub(self.query_list_rect.y.saturating_sub(1)); + + if offset >= self.state.query_sequences_list.len() as u16 { + return; + } + + self.dispatcher_tx + .send(state::Action::SetSelectedQuery(offset.into())) + .expect("Unable to send selected query sequence"); + } + } + + pub fn prepare(&mut self, terminal: &mut Terminal>) { + let screen_bg = Color::Rgb(0, 0, 0); + + terminal + .draw(|frame| { + Clear.render(frame.area(), frame.buffer_mut()); + Block::default() + .style(Style::default().bg(screen_bg)) + .render(frame.area(), frame.buffer_mut()); + }) + .expect("Error preparing"); + } + + pub async fn render(&mut self, terminal: &mut Terminal>) { self.frame_count += 1; - self.terminal.draw(|frame| { - let vertical = Layout::vertical([Length(1), Min(0), Length(1)]); - let [title_area, main_area, status_area] = vertical.areas(frame.area()); - let horizontal = Layout::horizontal([Fill(1), Fill(2)]); - let [left_area, right_area] = horizontal.areas(main_area); - - let mut count = 0; - - // Left area list of query sequence names - if self.state.query_sequences_list.is_empty() { - frame.render_widget(Text::styled("No query sequences", Style::default().fg(Color::Red)), left_area); - } else { - let mut items = vec![]; - for (i, query) in self.state.query_sequences_list.iter().enumerate() { - items.push(query.id.clone()); - } - count = self.state.query_sequences_list.len(); + let locale = SystemLocale::default().unwrap(); + + let mappings = if let Some(mappings) = &self.state.mappings { + Some(mappings.lock().await) + } else { + None + }; + + terminal + .draw(|frame| { + let screen_bg = Color::Rgb(0, 0, 0); + + let vertical = Layout::vertical([Length(1), Min(0), Length(1)]); + let [title_area, main_area, status_area] = vertical.areas(frame.area()); + let horizontal = Layout::horizontal([Fill(1), Fill(2)]); + let [left_area, right_area] = horizontal.areas(main_area); + + let mut count = 0; + + let border_style_default = Style::default().fg(Color::White); + let border_style_selected = Style::default().fg(Color::Yellow); + + let query_sequences_border_style = + if self.state.selected_panel == ui_state::SelectedPanel::QuerySequences { + border_style_selected + } else { + border_style_default + }; + + let mappings_border_style = + if self.state.selected_panel == ui_state::SelectedPanel::Mappings { + border_style_selected + } else { + border_style_default + }; + + // Left area list of query sequence names + if self.state.query_sequences_list.is_empty() { + let text = Text::styled("Indexing Reference", Style::default().fg(Color::LightRed)) + .bg(screen_bg); + let para = Paragraph::new(text) + .block( + Block::default().style(Style::default().bg(screen_bg)) + + ) + .alignment(Alignment::Center); + + frame.render_widget(para, left_area); + + // frame.render_widget(text, left_area); + + self.query_list_rect = left_area; + } else { + let mut items = vec![]; + for (i, query) in self.state.query_sequences_list.iter().enumerate() { + if self.state.selected_query_sequence.is_some() + && query.id == self.state.selected_query_sequence.as_ref().unwrap().id + { + self.query_list.select(Some(i)); + } + items.push(query.id.clone()); + } + count = self.state.query_sequences_list.len(); + + if self.query_list.selected().is_none() { + self.query_list.select_first(); + self.dispatcher_tx + .send(state::Action::SetSelectedQuery( + self.query_list.selected().unwrap(), + )) + .expect("Unable to send selected query sequence"); + } - if self.query_list.selected().is_none() { - self.query_list.select_first(); - self.dispatcher_tx.send(state::Action::SetSelectedQuery(self.query_list.selected().unwrap())).expect("Unable to send selected query sequence"); + let list = List::new(items) + .block( + Block::bordered() + .title("Query Sequences") + .style(Style::default().bg(screen_bg)) + .border_style(query_sequences_border_style), + ) + .style(Style::new().white()) + .highlight_style(Style::new().italic()) + .highlight_symbol(">>") + .repeat_highlight_symbol(true) + .bg(screen_bg) + .direction(ListDirection::TopToBottom); + + frame.render_stateful_widget(list, left_area, &mut self.query_list); + self.query_list_rect = left_area; + + /* + if self.left_area_effect.is_none() { + self.left_area_effect = Some(fx::sweep_in( + Direction::LeftToRight, + 30, + 0, + screen_bg, + (Duration::from_millis(1250), QuadOut), + ).with_area(left_area)); + } + + if let Some(effect) = self.left_area_effect.as_mut() { + frame.render_effect(effect, left_area, self.last_tick); + } + */ } - let list = List::new(items) - .block(Block::bordered().title("List")) - .style(Style::new().white()) - .highlight_style(Style::new().italic()) - .highlight_symbol(">>") - .repeat_highlight_symbol(true) - .direction(ListDirection::TopToBottom); + // Render the mappings + if let Some(mappings) = mappings { + let mut datasets = Vec::new(); + self.plot_data.clear(); - frame.render_stateful_widget(list, left_area, &mut self.query_list); - } + let (mut x_lower_bound, mut x_upper_bound) = (u64::MAX, u64::MIN); + let (mut y_lower_bound, mut y_upper_bound) = (u64::MAX, u64::MIN); - - - frame.render_widget(Block::bordered().title("Fakeminimap2"), title_area); - frame.render_widget(Block::bordered().title(format!("Status Bar - {} - {}", count, self.frame_count)), status_area); - frame.render_widget(Block::bordered().title("Query Sequences"), left_area); - frame.render_widget(Block::bordered().title("Mapping"), right_area); - - }).expect("Error rendering"); - } + // todo - need more separation between different target sequences + + let rows = mappings + .iter() + .enumerate() + .map(|(i, mapping)| { + x_lower_bound = x_lower_bound.min(mapping.query_start as u64); + x_upper_bound = x_upper_bound.max(mapping.query_end as u64); + y_lower_bound = y_lower_bound.min(mapping.target_start as u64); + y_upper_bound = y_upper_bound.max(mapping.target_end as u64); + + let data = vec![ + (mapping.query_start as f64, mapping.target_start as f64), + (mapping.query_end as f64, mapping.target_end as f64), + ]; + + self.plot_data.push(data.clone()); + + let text = mapping + .target_name + .as_ref() + .map(|s| s.to_string()) + .unwrap_or("".to_string()); + + let text = Text::styled(text, Style::default().fg(COLORS[i % COLORS.len()])); + + Row::new(vec![ + Cell::from(text), + Cell::from(mapping.query_start.to_formatted_string(&locale)), + Cell::from(mapping.query_end.to_formatted_string(&locale)), + Cell::from(mapping.target_start.to_formatted_string(&locale)), + Cell::from(mapping.target_end.to_formatted_string(&locale)), + Cell::from(mapping.match_len.to_formatted_string(&locale)), + ]).style(Style::default().bg(screen_bg).fg(COLORS[i % COLORS.len()])) + }) + .collect::>(); + + let widths = [ + Constraint::Fill(2), + Constraint::Fill(1), + Constraint::Fill(1), + Constraint::Fill(1), + Constraint::Fill(1), + Constraint::Fill(1), + ]; + + let table = Table::new(rows, widths) + .column_spacing(2) + .header( + Row::new(vec![ + Cell::from("Target"), + Cell::from("qStart"), + Cell::from("qEnd"), + Cell::from("tStart"), + Cell::from("tEnd"), + Cell::from("Match Len"), + ]) + .style(Style::new().bold()) + .bottom_margin(1), + ) + .style(Style::default().bg(screen_bg)) + .flex(Flex::SpaceAround) + .block( + Block::bordered() + .title("Mappings") + .border_style(mappings_border_style), + ); + + let vertical = Layout::vertical([Fill(2), Fill(6)]); + let [top_area, plot_area] = vertical.areas(right_area); - pub fn exit(&mut self) { - ratatui::restore(); + frame.render_widget(table, top_area); + + /* + if self.mappings_list_effect.is_none() { + self.mappings_list_effect = Some(fx::hsl_shift_fg( + [360.0, 0.0, 0.0], + Duration::from_millis(350), + )); + // Let's do a sweep in effect + self.mappings_list_effect = Some(fx::sweep_in( + Direction::RightToLeft, + 4, + 128, + screen_bg, + (Duration::from_millis(1250), QuadOut), + ).with_area(top_area)); + } */ + + self.plot_data.iter().enumerate().for_each(|(i, data)| { + let dataset = Dataset::default() + // .name(format!("Mapping {}", i)) + .graph_type(GraphType::Line) + .marker(symbols::Marker::Braille) + .data(&data) + .style(Style::default().fg(COLORS[i % COLORS.len()])); + + datasets.push(dataset); + }); + + let x_axis = Axis::default() + .title("Query".red()) + .style(Style::default().white()) + .bounds([x_lower_bound as f64, x_upper_bound as f64]); + + let y_axis = Axis::default() + .title("Target".blue()) + .style(Style::default().white()) + .bounds([y_lower_bound as f64, y_upper_bound as f64]); + + let chart = Chart::new(datasets) + .block(Block::new().title("Chart").style(Style::default().bg(screen_bg))) + .x_axis(x_axis) + .y_axis(y_axis) + .style(Style::default().bg(screen_bg)); + + /* + if self.plot_area_effect.is_none() { + self.plot_area_effect = Some(fx::sweep_in( + Direction::UpToDown, + 6, + 128, + COLORS[1], + Duration::from_millis(550), + ).with_area(plot_area)); + } + */ + + frame.render_widget(chart, plot_area); + + /* + if let Some(effect) = self.mappings_list_effect.as_mut() { + frame.render_effect(effect, top_area, self.last_tick); + } */ + + } else { + frame.render_widget( + Text::styled("No mappings", Style::default().fg(Color::Red)), + right_area, + ); + } + + frame.render_widget(Block::bordered().title("Fakeminimap2"), title_area); + + frame.render_widget( + Block::bordered().title(format!( + "{} - {} Entries - {} Frame Renders", + self.state.status, count, self.frame_count + )), + status_area, + ); + }) + .expect("Error rendering"); } -} \ No newline at end of file +} From 691beacc060fe8631fe3ef470a9238b279bc8c32 Mon Sep 17 00:00:00 2001 From: Joseph Guhlin Date: Tue, 3 Dec 2024 20:15:22 +1300 Subject: [PATCH 3/8] Keyboard shortcuts --- .../src/state/query_sequences_store.rs | 5 +- fakeminimap2/src/ui.rs | 10 +-- fakeminimap2/src/ui/app_display.rs | 72 +++++++++++++++++-- 3 files changed, 75 insertions(+), 12 deletions(-) diff --git a/fakeminimap2/src/state/query_sequences_store.rs b/fakeminimap2/src/state/query_sequences_store.rs index f028cf3..429f695 100644 --- a/fakeminimap2/src/state/query_sequences_store.rs +++ b/fakeminimap2/src/state/query_sequences_store.rs @@ -29,7 +29,7 @@ impl QuerySequencesStore { // Set selected mappings self.dispatcher_tx .send(Action::SetSelectedMappings( - self.query_sequences[n].id.clone(), + self.query_sequences[self.current].id.clone(), )) .expect("Unable to send selected mappings"); } @@ -52,6 +52,9 @@ impl QuerySequencesStore { /// Set the currently viewed QuerySequence pub fn set_current(&mut self, n: usize) { + // Clamp + let n = n.min(self.query_sequences.len() - 1); + let n = n.max(0); self.current = n; let _ = self .dispatcher_tx diff --git a/fakeminimap2/src/ui.rs b/fakeminimap2/src/ui.rs index de0ec2a..076f517 100644 --- a/fakeminimap2/src/ui.rs +++ b/fakeminimap2/src/ui.rs @@ -49,14 +49,17 @@ pub async fn main_loop( last_frame_instant = Instant::now(); tokio::select! { - _ = ticker.tick() => (), + _ = ticker.tick() => { + app_display.render(&mut terminal).await; + }, Some(state) = ui_rx.next() => { // Update the state app_display.set_state(state.unwrap()); + app_display.render(&mut terminal).await; }, evt = crossterm_events.next() => match evt { Some(Ok(Event::Key(key))) => { - dispatcher_tx.send(state::Action::KeyPress(key)).expect("Unable to send key press event"); + app_display.handle_keypress(key).await; }, Some(Ok(Event::Mouse(evt))) => { let col = evt.column; @@ -67,7 +70,6 @@ pub async fn main_loop( app_display.handle_click(col, row); }, MouseEventKind::Up(_) => { - // }, MouseEventKind::Moved => { } @@ -83,8 +85,6 @@ pub async fn main_loop( if app_display.should_quit() { break; } - - app_display.render(&mut terminal).await; } restore_terminal(&mut terminal).expect("Unable to restore terminal"); diff --git a/fakeminimap2/src/ui/app_display.rs b/fakeminimap2/src/ui/app_display.rs index 25b40cb..764afe5 100644 --- a/fakeminimap2/src/ui/app_display.rs +++ b/fakeminimap2/src/ui/app_display.rs @@ -18,7 +18,7 @@ const COLORS: [Color; 6] = [ // Pink, Yellow, Brighter Pink, Brighter Yellow, Darker Pink, Darker Yellow Color::Rgb(255, 62, 181), Color::Rgb(255, 233, 0), - Color::Rgb(201, 0, 124), + Color::Rgb(197, 0, 102), Color::Rgb(255, 243, 112), Color::Rgb(255, 112, 200), Color::Rgb(156, 142, 0) @@ -34,6 +34,8 @@ pub struct AppDisplay { left_area_effect: Option, mappings_list_effect: Option, plot_area_effect: Option, + + query_list_height: usize, pub last_tick: Duration, pub plot_data: Vec>, @@ -56,6 +58,7 @@ impl AppDisplay { plot_area_effect: None, last_tick: Duration::ZERO, plot_data: Vec::new(), + query_list_height: 0, } } @@ -73,6 +76,56 @@ impl AppDisplay { self.state = state; } + pub async fn handle_keypress(&mut self, key: event::KeyEvent) { + + // Only if the selected panel is QuerySequences + if self.state.selected_panel == ui_state::SelectedPanel::QuerySequences { + // todo, replace with a function + // PgUp and PgDown + match key.code { + event::KeyCode::PageUp => { + let new_selection = self.query_list.selected().unwrap_or(0).saturating_sub(self.query_list_height); + self.query_list.select(Some(new_selection)); + self.dispatcher_tx + .send(state::Action::SetSelectedQuery(new_selection.into())) + .expect("Unable to send selected query sequence"); + return + }, + event::KeyCode::PageDown => { + let new_selection = self.query_list.selected().unwrap_or(0) + self.query_list_height; + self.query_list.select(Some(new_selection)); + self.dispatcher_tx + .send(state::Action::SetSelectedQuery(new_selection.into())) + .expect("Unable to send selected query sequence"); + return + }, + // Home and End Keys + event::KeyCode::Home => { + self.query_list.select(Some(0)); + self.dispatcher_tx + .send(state::Action::SetSelectedQuery(0)) + .expect("Unable to send selected query sequence"); + return + }, + event::KeyCode::End => { + let new_selection = self.state.query_sequences_list.len().saturating_sub(1); + self.query_list.select(Some(new_selection)); + self.dispatcher_tx + .send(state::Action::SetSelectedQuery(new_selection)) + .expect("Unable to send selected query sequence"); + return + }, + _ => (), + } + } + + // Otherwise bubble it to the dispatcher + self.dispatcher_tx + .send(state::Action::KeyPress(key)) + .expect("Unable to send key press event"); + + } + pub fn handle_click(&mut self, col: u16, row: u16) { if self.query_list_rect.contains(Position { x: col, y: row }) { // Calculate offset from the top of the list @@ -146,17 +199,15 @@ impl AppDisplay { let para = Paragraph::new(text) .block( Block::default().style(Style::default().bg(screen_bg)) - ) .alignment(Alignment::Center); frame.render_widget(para, left_area); - // frame.render_widget(text, left_area); - self.query_list_rect = left_area; } else { let mut items = vec![]; + let selected; for (i, query) in self.state.query_sequences_list.iter().enumerate() { if self.state.selected_query_sequence.is_some() && query.id == self.state.selected_query_sequence.as_ref().unwrap().id @@ -176,12 +227,15 @@ impl AppDisplay { .expect("Unable to send selected query sequence"); } + selected = self.query_list.selected().unwrap_or(0); + let list = List::new(items) .block( Block::bordered() .title("Query Sequences") .style(Style::default().bg(screen_bg)) - .border_style(query_sequences_border_style), + .border_style(query_sequences_border_style) + .title_bottom(format!("Query Sequences {}/{}", selected, count)) ) .style(Style::new().white()) .highlight_style(Style::new().italic()) @@ -193,6 +247,11 @@ impl AppDisplay { frame.render_stateful_widget(list, left_area, &mut self.query_list); self.query_list_rect = left_area; + // How many query sequences are shown on a single screen? + let query_list_height = left_area.height as usize - 2; + self.query_list_height = query_list_height; + + /* if self.left_area_effect.is_none() { self.left_area_effect = Some(fx::sweep_in( @@ -329,7 +388,8 @@ impl AppDisplay { .bounds([y_lower_bound as f64, y_upper_bound as f64]); let chart = Chart::new(datasets) - .block(Block::new().title("Chart").style(Style::default().bg(screen_bg))) + .block(Block::new().title("Chart").style(Style::default().bg(screen_bg)) + .title_bottom(format!("{} Mappings", mappings.len()))) .x_axis(x_axis) .y_axis(y_axis) .style(Style::default().bg(screen_bg)); From 5d5ece1f60551c00dd0422e506f8a29dcf13d503 Mon Sep 17 00:00:00 2001 From: Joseph Guhlin Date: Tue, 3 Dec 2024 20:16:02 +1300 Subject: [PATCH 4/8] Move the Mappings count entry --- fakeminimap2/src/ui/app_display.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fakeminimap2/src/ui/app_display.rs b/fakeminimap2/src/ui/app_display.rs index 764afe5..cde8dc0 100644 --- a/fakeminimap2/src/ui/app_display.rs +++ b/fakeminimap2/src/ui/app_display.rs @@ -342,7 +342,8 @@ impl AppDisplay { .block( Block::bordered() .title("Mappings") - .border_style(mappings_border_style), + .border_style(mappings_border_style) + .title_bottom(format!("{} Mappings", mappings.len())), ); let vertical = Layout::vertical([Fill(2), Fill(6)]); @@ -388,8 +389,7 @@ impl AppDisplay { .bounds([y_lower_bound as f64, y_upper_bound as f64]); let chart = Chart::new(datasets) - .block(Block::new().title("Chart").style(Style::default().bg(screen_bg)) - .title_bottom(format!("{} Mappings", mappings.len()))) + .block(Block::new().title("Chart").style(Style::default().bg(screen_bg))) .x_axis(x_axis) .y_axis(y_axis) .style(Style::default().bg(screen_bg)); From 5ffe2a8aa9b04f5c893e44a32e688a8d54e9fa16 Mon Sep 17 00:00:00 2001 From: Joseph Guhlin Date: Tue, 3 Dec 2024 20:29:06 +1300 Subject: [PATCH 5/8] Fix mouse clicks --- fakeminimap2/src/ui/app_display.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fakeminimap2/src/ui/app_display.rs b/fakeminimap2/src/ui/app_display.rs index cde8dc0..ea39ee5 100644 --- a/fakeminimap2/src/ui/app_display.rs +++ b/fakeminimap2/src/ui/app_display.rs @@ -129,7 +129,7 @@ impl AppDisplay { pub fn handle_click(&mut self, col: u16, row: u16) { if self.query_list_rect.contains(Position { x: col, y: row }) { // Calculate offset from the top of the list - let offset = row.saturating_sub(self.query_list_rect.y.saturating_sub(1)); + let offset = row.saturating_sub(self.query_list_rect.y).saturating_sub(1); if offset >= self.state.query_sequences_list.len() as u16 { return; From a77a7f118c2755de7fdb870bd53b9b294149c280 Mon Sep 17 00:00:00 2001 From: Joseph Guhlin Date: Wed, 4 Dec 2024 08:38:22 +1300 Subject: [PATCH 6/8] Update versions --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a481da9..46fec14 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ minimap2-sys is the raw FFI bindings to minimap2. minimap2 is the more opinionat # How to use ## Requirements ```toml -minimap2 = "0.1.21+minimap2.2.28" +minimap2 = "0.1.22+minimap2.2.28" ``` Also see [Features](#features) @@ -20,6 +20,7 @@ Tested with rustc 1.82.0 and nightly. So probably a good idea to upgrade before ## Minimap2 Version Table | minimap2-rs | minimap2 | |-------------|----------| +| 0.1.22 | 2.28 | | 0.1.21 | 2.28 | | 0.1.20 | 2.28 | | 0.1.19 | 2.28 | From a438c200ccc28ab4961288a837f0be756cfe0cc9 Mon Sep 17 00:00:00 2001 From: Joseph Guhlin Date: Wed, 4 Dec 2024 08:42:43 +1300 Subject: [PATCH 7/8] Update deps --- fakeminimap2/Cargo.lock | 75 +++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/fakeminimap2/Cargo.lock b/fakeminimap2/Cargo.lock index 188fb9c..8a8e4d3 100644 --- a/fakeminimap2/Cargo.lock +++ b/fakeminimap2/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[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" @@ -159,9 +159,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bon" -version = "3.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e47d5c63335658326076cf7c81795af665c534ea552da69526d6cef51b12ed9" +checksum = "9276fe602371cd8a7f70fe68c4db55b2d3e92c570627d6ed0427646edfa5cf47" dependencies = [ "bon-macros", "rustversion", @@ -169,9 +169,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b162272b6d55562ea30cc937d74ef4d07399e507bfd6eb3860f6a845c7264eef" +checksum = "94828b84b32b4f3ac3865f692fcdbc46c7d0dd87b29658a391d58a244e1ce45a" dependencies = [ "darling", "ident_case", @@ -179,7 +179,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -205,9 +205,9 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[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 = "bzip2" @@ -247,9 +247,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 = [ "jobserver", "libc", @@ -325,7 +325,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -478,7 +478,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -489,7 +489,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -535,12 +535,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]] @@ -646,12 +646,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 = "home" version = "0.5.9" @@ -713,7 +707,7 @@ dependencies = [ "pretty_assertions", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -770,9 +764,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[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 = "libloading" @@ -888,11 +882,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", @@ -1046,7 +1039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1239,9 +1232,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", @@ -1278,7 +1271,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1294,9 +1287,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", @@ -1332,14 +1325,14 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[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", @@ -1361,7 +1354,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1464,7 +1457,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -1486,7 +1479,7 @@ checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] From 650b226b06cee0e1114404b297ae96290e04c50f Mon Sep 17 00:00:00 2001 From: Joseph Guhlin Date: Wed, 4 Dec 2024 08:57:38 +1300 Subject: [PATCH 8/8] How to run --- fakeminimap2/README.md | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/fakeminimap2/README.md b/fakeminimap2/README.md index a03d6d1..d890c69 100644 --- a/fakeminimap2/README.md +++ b/fakeminimap2/README.md @@ -1,11 +1,34 @@ # Multithreaded example of mapping -Using crossbeam and standard library threading, we can multi thread alignments sharing the same references to the index. -Small example files are provided, +Using crossbeam and standard library threading, we can multi thread alignments sharing the same references to the index. Small example files are provided. + +## Running +Make sure you have [Rustup](https://rustup.rs/) installed. + +Git clone the whole repo and go to the fakeminimap2 directory. +``` +cargo run --release +``` + +Such as +``` +cargo run --release Arabidopsis.fna reads.fasta 64 +``` + +You can also +``` +cargo install --path . +``` +To install it, then run it as + +``` +fakeminimap2 the-best-bird.fasta new-reads.fasta 32 +``` ## Logging Logging is done using the log crate, and the log level can be set using the RUST_LOG environment variable. ``` RUST_LOG=info cargo run -``` \ No newline at end of file +``` +