commit 6f4ef18b0b155a551707e7644c978c80e9dcb588 Author: Avril Date: Thu Nov 24 09:10:44 2022 +0000 initial commit Added file sorter Started skeleton for fs walker Fortune for enumerate-ordered's current commit: Blessing − 吉 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..207f25c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,650 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cc" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", +] + +[[package]] +name = "enumerate-ordered" +version = "0.1.0" +dependencies = [ + "color-eyre", + "futures", + "log", + "pretty_env_logger", + "tokio", + "tokio-stream", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "futures" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-executor" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "num_cpus" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +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.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "tokio" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[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-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[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.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..96fd40b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "enumerate-ordered" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +color-eyre = { version = "0.6.2", default-features = false } +futures = "0.3.25" +log = "0.4.17" +pretty_env_logger = "0.4.0" +tokio = { version = "1.22.0", features = ["full"] } +tokio-stream = { version = "0.1.11", features = ["sync", "fs", "io-util", "net", "signal", "tokio-util"] } diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..32ac3e1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,87 @@ + +#[macro_use] extern crate log; + +use color_eyre::{ + eyre::{ + self, + eyre, + WrapErr as _, + }, + SectionExt as _, + Help as _, +}; +use tokio::{ + sync::{ + mpsc, + }, +}; + +mod order; +mod work; +mod walk; + +fn init_logging() -> eyre::Result<()> +{ + color_eyre::install()?; + pretty_env_logger::init(); + Ok(()) +} + +async fn work_on(cfg: work::Config, mut into: mpsc::Receiver) -> eyre::Result +{ + use work::*; + let mut map = FSTimeMap::new(cfg); + while let Some(file) = into.recv().await { + if cfg!(debug_assertions) { + trace!("insert +{}", map.len()); + } + map.insert(file); + } + Ok(map) +} + + +#[tokio::main] +async fn main() -> eyre::Result<()> { + init_logging().wrap_err("Failed to set logging handlers")?; + + let (tx, rx) = mpsc::channel(4096); + + // Spin up ordering task. + let ordering = tokio::spawn(async move { + trace!("spun up ordering backing thread"); + work_on(Default::default(), rx).await //TODO: Parse config from args + }); + + trace!("Starting recursive walk of input locations"); + //TODO: Trace directory trees from paths in `args` and/or `stdin` and pass results to `tx` + + + let set = ordering.await.wrap_err("Ordering task panic")? + .wrap_err(eyre!("Failed to collect ordered files"))?; + + tokio::task::spawn_blocking(move || -> eyre::Result<()> { + use std::io::Write; + use std::os::unix::prelude::*; + let mut stdout = { + let lock = std::io::stdout().lock(); + std::io::BufWriter::new(lock) + }; + + for info in set.into_iter() + { + stdout.write_all(info.path().as_os_str().as_bytes()) + .and_then(|_| stdout.write_all(&[b'\n'])) + .wrap_err("Failed to write raw pathname for entry to stdout") + .with_context(|| format!("{:?}", info.path()).header("Pathname was"))?; + } + + stdout.flush().wrap_err("Failed to flush buffered output to stdout")?; + Ok(()) + }).await.wrap_err("Writer task panic")? + .wrap_err("Failed to write results to stdout")?; + + trace!("Finished output task"); + + Ok(()) +} diff --git a/src/order.rs b/src/order.rs new file mode 100644 index 0000000..a31bdc0 --- /dev/null +++ b/src/order.rs @@ -0,0 +1,137 @@ +//! Ordered value wrapper +use super::*; +use std::{ + cmp::Ordering, + marker::PhantomData, + borrow::{ + Borrow, BorrowMut, + }, + ops, +}; + +/// Provides external absolute ordering for references to type `T`. +pub trait Orderer +{ + fn order(a: &T, b: &T) -> Ordering; +} + +/// The default `Orderer` used in `Ordered`. +/// Implemented for all `T: Ord`. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)] +pub enum DefaultOrderProvider{} + +impl Orderer for DefaultOrderProvider +{ + #[inline] + fn order(a: &T, b: &T) -> Ordering { + Ord::cmp(a, b) + } +} + +/// Provides an external ordering for a value `T` +#[derive(Debug, Clone, Hash, Copy)] +pub struct Ordered = DefaultOrderProvider>(PhantomData>, T); + +impl> Ordered +{ + #[inline] + pub fn new(value: T) -> Self + { + Self(PhantomData, value) + } + #[inline] + pub fn into_inner(self) -> T + { + self.1 + } +} + +impl> Ordered +{ + #[inline] + pub fn inner(&self) -> &T + { + &self.1 + } + #[inline] + pub fn inner_mut(&mut self) -> &mut T + { + &mut self.1 + } +} + +impl> Borrow for Ordered +{ + #[inline] + fn borrow(&self) -> &T { + self.inner() + } +} + +impl> BorrowMut for Ordered +{ + #[inline] + fn borrow_mut(&mut self) -> &mut T { + self.inner_mut() + } +} + +impl> AsRef for Ordered +{ + #[inline] + fn as_ref(&self) -> &T { + self.inner() + } +} + +impl> AsMut for Ordered +{ + #[inline] + fn as_mut(&mut self) -> &mut T { + self.inner_mut() + } +} + +impl> ops::Deref for Ordered +{ + type Target = T; + #[inline] + fn deref(&self) -> &T { + self.inner() + } +} + +impl> ops::DerefMut for Ordered +{ + #[inline] + fn deref_mut(&mut self) -> &mut T { + self.inner_mut() + } +} + +impl Eq for Ordered where O: Orderer{} +impl PartialEq for Ordered +where O: Orderer +{ + #[inline] + fn eq(&self, other: &Self) -> bool + { + O::order(&self.1, &other.1).is_eq() + } +} +impl Ord for Ordered +where O: Orderer +{ + #[inline] + fn cmp(&self, other: &Self) -> Ordering + { + O::order(&self.1, &other.1) + } +} +impl PartialOrd for Ordered +where O: Orderer +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(O::order(&self.1, &other.1)) + } +} diff --git a/src/walk.rs b/src/walk.rs new file mode 100644 index 0000000..38631b9 --- /dev/null +++ b/src/walk.rs @@ -0,0 +1,117 @@ +//! Directory walking +use super::*; +use std::num::NonZeroUsize; +use std::{ + sync::Arc, + path::{ + Path, PathBuf, + }, +}; +use tokio::{ + sync::{ + RwLock, + Semaphore, + }, +}; +use futures::future::{ + Future, + OptionFuture, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] +pub struct Config +{ + // None: unlimited. 0: no recursion + pub recursion_depth: Option, + // None: Unlimited tasks. + pub max_walkers: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +struct UniqueState +{ + // Decrease with each new depth until 0 is hit then do not proceed. + recursion_depth: Option, +} + +#[derive(Debug)] +struct SharedState +{ + config: Config, + output_sender: mpsc::Sender, + new_worker_semaphore: Option, +} + +#[derive(Debug, Clone)] +struct State { + /// Shared state for all iterations + shared: Arc, //TODO: XXX: *another* Arc slowdown... + /// State for current iteration + current: UniqueState, +} + +/// Walk a single directory in the current task. Dispatch async results to `walk +async fn walk_directory(state: &State, whence: impl AsRef) -> eyre::Result +{ + todo!("Walk to directory and output its files into `state`'s tx XXX: Does this function need to exist? We could just do this in walk_inner() directly: Explicit boxing doesn't need to be done as we're working with joinhandles and backing tasks") +} + +/// This function is called recursively for each subdirectory in `whence` pertaining to the recursion rules. +/// The function should await all its spawned children *after* finishing its own work on files, **and** dropping its semaphore `_permit`. Otherwise deadlock could occour easily. +async fn walk_inner(state: State, whence: PathBuf) -> impl Future> + 'static +{ + // Acquire permit *before* spawning. + let _permit = { + OptionFuture::from(state.shared.new_worker_semaphore.as_ref().map(|sem| sem.acquire())).map(|opt| match opt { + Some(Err(e)) => { + #[cold] + #[inline(never)] + fn _panic_permit(e: impl std::fmt::Display) -> ! { + panic!("Failed to attempt to acquire walker permit: {}", e) + } + _panic_permit(e) + }, + Some(Ok(p)) => Some(p), + None => None, + }) + }.await; + use futures::prelude::*; + async move { + let backing_res = tokio::spawn(async move { + // Move permit into task once acquires. + let _permit = _permit; + // Number of *files* sent to tx from this iteration. + let mut counted = 0usize; + // `JoinHandle>`s to recursion children + // XXX: Maybe use mpsc for this instead: We send the JoinHandle's to a rx being looped on in a `join!` in the outer `async move {}` scope at the same time as this one. When the sender is dropped, the channel will close. We can join each child of the stream concurrently with `futures` (probably?) and bubble up panics when they are found. + let mut children = Vec::new(); + + todo!("counted += walk_directory(&state, &whence).await, etc..."); + Ok((counted, children)) + }).await.expect("Panic in backing walker thread"); + + match backing_res { + Ok((counted, children)) => { + Ok(counted + futures::future::join_all(children.into_iter()).await.into_iter().sum::()) + }, + Err(e) => Err(e), + } + } +} + +#[inline] +pub fn start_walk(cfg: Config, whence: impl AsRef, whereto: mpsc::Sender) -> impl Future> + 'static +{ + use futures::prelude::*; + walk_inner(State { + current: UniqueState { + recursion_depth: cfg.recursion_depth, + }, + shared: Arc::new(SharedState { + output_sender: whereto, + new_worker_semaphore: cfg.max_walkers.as_ref().map(|max| Semaphore::new(max.get())), + config: cfg + }), + }, whence.as_ref().to_owned()) + .flatten() +} diff --git a/src/work.rs b/src/work.rs new file mode 100644 index 0000000..4762343 --- /dev/null +++ b/src/work.rs @@ -0,0 +1,148 @@ +//! Works ordering the file system objects +use super::*; +use std::{ + collections::BTreeSet, + path::{ + PathBuf, + Path, + }, + fs::Metadata, + sync::Arc, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Default)] +pub enum OrderBy +{ + #[default] + CreationTime, + AccessTime, + ModifiedTime, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +pub struct Config +{ + pub by: OrderBy, +} + +/// Information from a file +#[derive(Debug, Clone)] +pub struct FileInfo +{ + state: Arc, //TODO: XXX: This will be a *severe* slowdown... How can we have just a reference here? Thread local? `tokio::task_local!`? Global static? + path: PathBuf, + meta: Metadata, +} + +impl FileInfo +{ + /// Returns a factory function that produces `FileInfo`s with this config. + #[inline] + pub fn factory<'a, T: Into>>(config: T) -> impl Fn(PathBuf, Metadata) -> FileInfo + 'a + where T: 'a + { + let v = config.into(); + move |path, meta| -> FileInfo { + FileInfo::new(Arc::clone(&v), path, meta) + } + } + + #[inline] + pub fn new(config: impl Into>, path: PathBuf, meta: Metadata) -> Self + { + Self { + state: config.into(), + path, meta + } + } + + #[inline] + pub fn path(&self) -> &Path + { + &self.path + } + + #[inline] + pub fn metadata(&self) -> &Metadata + { + &self.meta + } + + #[inline] + pub fn config(&self) -> &Config + { + &self.state + } + + #[inline] + pub fn config_cell(&self) -> &Arc + { + &self.state + } +} + +impl FileInfo +{ + #[inline] + fn get_ordered_unix_time(&self) -> i64 + { + use std::os::unix::prelude::*; + match self.state.by { + OrderBy::AccessTime => self.meta.atime(), + OrderBy::CreationTime => self.meta.ctime(), + OrderBy::ModifiedTime => self.meta.mtime(), + } //XXX: Should we support nsec precision? + } +} + +impl order::Orderer for FileInfo +{ + #[inline(always)] + fn order(a: &FileInfo, b: &FileInfo) -> std::cmp::Ordering { + match Ord::cmp(&a.get_ordered_unix_time(), &b.get_ordered_unix_time()) { + std::cmp::Ordering::Equal => { + // If times are equal, compare by path to avoid all files with same timecode overwriting eachother. + Ord::cmp(a.path(), b.path()) + //TODO: How to handle the `-R` case nested ordering... Uhh... Recursive interface for `FSTimeMap`, like `NestedFSTimeMap` or something? + }, + non_eq => non_eq, + } + } +} + +/// Maps files based on their time +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] +pub struct FSTimeMap(Arc, BTreeSet>); + +impl FSTimeMap +{ + #[inline] + pub fn new(config: impl Into) -> Self + { + Self(Arc::new(config.into()), BTreeSet::new()) + } + + #[inline] + pub fn iter(&self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator + std::iter::FusedIterator + '_ + { + self.1.iter().map(|x| x.inner()) + } + + #[inline] + pub fn into_iter(self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator + std::iter::FusedIterator + 'static + { + self.1.into_iter().map(|x| x.into_inner()) + } + + #[inline] + pub fn insert(&mut self, file: FileInfo) + { + self.1.insert(order::Ordered::new(file)); + } + + #[inline] + pub fn len(&self) -> usize + { + self.1.len() + } +}