From 1334d9f1d08a27ea7fa4cd4228ac1fdf44bdd552 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Tue, 1 Nov 2022 17:48:54 -0500 Subject: feat: impl ignore crate to walk directories --- Cargo.lock | 520 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 8 +- src/cli.rs | 39 +++-- src/config.rs | 247 ++++++++++++++++++++-------- src/error.rs | 9 + src/lib.rs | 22 +++ src/main.rs | 4 +- 7 files changed, 765 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7dc044a..e5e0e85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,60 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +[[package]] +name = "async-channel" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock", + "autocfg", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-lock" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +dependencies = [ + "event-listener", + "futures-lite", +] + +[[package]] +name = "async-task" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" + [[package]] name = "atomic" version = "0.5.1" @@ -17,6 +65,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + [[package]] name = "atty" version = "0.2.14" @@ -40,6 +94,41 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "blocking" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -83,6 +172,55 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "concurrent-queue" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs" version = "4.0.0" @@ -103,6 +241,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + [[package]] name = "figment" version = "0.10.8" @@ -110,13 +263,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e56602b469b2201400dec66a66aec5a9b8761ee97cd1b8c96ab2483fcc16cc9" dependencies = [ "atomic", + "parking_lot 0.12.1", "pear", "serde", + "tempfile", "toml", "uncased", "version_check", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -128,6 +335,25 @@ dependencies = [ "wasi", ] +[[package]] +name = "globset" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "heck" version = "0.4.0" @@ -143,12 +369,49 @@ dependencies = [ "libc", ] +[[package]] +name = "ignore" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "inlinable_string" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -161,6 +424,16 @@ 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" @@ -170,6 +443,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -198,6 +486,60 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.5", +] + +[[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 0.9.4", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[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 = "pear" version = "0.2.3" @@ -227,6 +569,15 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +[[package]] +name = "plain_path" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1b940aa8e0562ece01eb12a9731bb8f6f0325c2c97c8629f852504f01d4537" +dependencies = [ + "dirs 3.0.2", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -302,6 +653,47 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.147" @@ -331,12 +723,40 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "sled" +version = "0.34.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot 0.11.2", +] + [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "ssh_cfg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33130e7e8a61e5822f5f6f7f7127e4adad805fcc1a55dc83d2b5c15c1279014c" +dependencies = [ + "async-fs", + "dirs 4.0.0", + "indexmap", + "plain_path", +] + [[package]] name = "strsim" version = "0.10.0" @@ -354,6 +774,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -392,16 +826,26 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tmux_interface" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f52fd871ec97d0c84c28fcaa717d63636ca1459c4dafb17a97114f3687ab0" + [[package]] name = "tmuxr" version = "0.1.0" dependencies = [ "anyhow", "clap", - "dirs", + "dirs 4.0.0", "figment", + "ignore", "serde", + "sled", + "ssh_cfg", "thiserror", + "tmux_interface", "tracing", "tracing-subscriber", ] @@ -500,6 +944,23 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -537,6 +998,63 @@ 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" + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 9639313..eac7078 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,14 @@ license = "MIT" anyhow = "1.0.66" clap = { version = "4.0.18", features = ["derive", "env"] } dirs = "4.0.0" -figment = { version = "0.10.8", features = ["toml", "env"] } +figment = { version = "0.10.8", features = ["toml", "env", "test"] } +ignore = "0.4.18" serde = { version = "1.0.147", features = ["derive"] } +sled = "0.34.7" +ssh_cfg = { version = "0.3.0", optional = true } thiserror = "1.0.37" +tmux_interface = { version = "0.2.1", default-features = false, features = [ + "tmux_latest", +] } tracing = { version = "0.1.37", features = ["attributes"] } tracing-subscriber = "0.3.16" diff --git a/src/cli.rs b/src/cli.rs index 5da1036..68bc665 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,22 +1,29 @@ -use crate::{Config, Result}; +use crate::{config::Paths, Config, Result}; use clap::{Args, Parser}; -use std::fs::File; -use std::sync::Arc; +use std::path::PathBuf; use tracing_subscriber::{filter::LevelFilter, Layer, Registry}; /// Simple program to manage projects and ssh hosts using tmux #[derive(Parser, Debug)] #[command(author, version, about)] pub struct Cli { + /// Path to search recursively for directories + pub(crate) path: Vec, + + /// Add additional directory to search results. Can be specified multiple times + #[arg(short, long)] + pub(crate) directory: Vec, + #[command(flatten)] pub verbose: Verbosity, + /// Allows traversal into hidden directories when searching + #[arg(long)] + pub(crate) hidden: bool, + /// Connect to ssh host #[arg(short, long)] pub ssh: Option, - - #[command(flatten)] - pub config: Config, } impl Cli { @@ -30,16 +37,20 @@ impl Cli { layers.push(fmt_layer); - if self.config.enable_logging { - let file = File::create(&self.config.log_file)?; - let log_layer = tracing_subscriber::fmt::layer() - .with_writer(Arc::new(file)) - .boxed(); - layers.push(log_layer); - }; - Ok(layers) } + + pub fn as_config(&self) -> Config { + Config { + paths: Paths { + search: self.path.to_owned(), + add: self.directory.to_owned(), + hidden: self.hidden, + }, + finder: Default::default(), + logging: Default::default(), + } + } } #[derive(Debug, Default, Args)] diff --git a/src/config.rs b/src/config.rs index 42614ec..2c61b88 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,64 +1,99 @@ use crate::{Error, Result}; -use clap::{Args, Parser}; use figment::{ providers::{Env, Format, Serialized, Toml}, Figment, }; +use ignore::WalkBuilder; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +use std::{fmt::Display, fs::File, path::PathBuf, sync::Arc}; +use tracing::{info, warn}; +use tracing_subscriber::{Layer, Registry}; -/// Simple program to manage projects and ssh hosts using tmux -#[derive(Debug, Parser, Serialize, Deserialize)] -#[command(author, version, about)] +#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct Config { - /// Path to search for directories - pub(crate) path: Vec, - - /// Stand alone directory. Can be specified multiple times - #[arg(short, long)] - pub(crate) project: Vec, - - /// Allows traversal into hidden directories when searching - #[arg(long)] - pub(crate) hidden: bool, - - #[arg(skip)] + pub(crate) paths: Paths, pub(crate) finder: Finder, - - #[arg(skip)] - pub(crate) enable_logging: bool, - - #[arg(skip)] - pub(crate) log_file: PathBuf, + pub(crate) logging: Logging, } impl Config { - pub fn extract(&self) -> Result { + pub fn extract(self) -> Result { Figment::from(Serialized::defaults(self)) .merge(Toml::file("tmuxr.toml")) .merge(Env::prefixed("TMUXR_")) .extract() .map_err(Error::from) } + + pub fn as_layer(&self) -> Result + Send + Sync>>> { + let mut layers = Vec::new(); + + if self.logging.enabled { + let file = File::create(&self.logging.path)?; + let log_layer = tracing_subscriber::fmt::layer() + .with_writer(Arc::new(file)) + .boxed(); + layers.push(log_layer); + }; + + Ok(layers) + } } -impl Default for Config { - fn default() -> Self { - Self { - path: Default::default(), - project: Default::default(), - hidden: Default::default(), - finder: Default::default(), - enable_logging: Default::default(), - log_file: dirs::cache_dir() - .map(|p| p.join("tmuxr")) - .unwrap_or_default() - .join("tmuxr.log"), - } +#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +pub(crate) struct Paths { + pub(crate) search: Vec, + pub(crate) add: Vec, + pub(crate) hidden: bool, +} + +impl Display for Paths { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Vec::::from(self) + .into_iter() + .filter_map(|p: PathBuf| { + p.to_str().map(|s| s.to_owned()).or_else(|| { + warn!("Failed to convert path to str"); + None + }) + }) + .map(|s| writeln!(f, "{}", s)) + .collect::() + } +} + +impl From<&Paths> for Vec { + fn from(value: &Paths) -> Self { + let mut dirs = value.search.to_owned().into_iter(); + let first = dirs.nth(0).unwrap_or_default(); + + let mut path_bufs = dirs + .fold( + WalkBuilder::new(first) + .standard_filters(true) + .max_depth(Some(1)), + |walk, dir| walk.add(dir), + ) + .build() + .filter_map(|res| { + res.map_err(|err| { + warn!("Error while walking directory: {:?}", err); + err + }) + .ok() + }) + .map(|entry| entry.into_path()) + .collect::>(); + + info!("{:?}", value.add); + info!("{:?}", path_bufs); + + path_bufs.extend(value.add.to_owned()); + path_bufs } } -#[derive(Debug, Args, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub(crate) struct Finder { pub(crate) program: String, pub(crate) args: Vec, @@ -69,46 +104,126 @@ impl Default for Finder { Self { program: "fzf-tmux".into(), args: vec![ - "--".into(), - "--multi".into(), - "--print-query".into(), - "-d/".into(), - "--preview-window='right,75%,<80(up,75%,border-bottom)'".into(), - "--preview='sel={}; less ${sel:-{q}} 2>/dev/null'".into(), - ], + "--", + "--multi", + "--print-query", + "-d/", + "--preview-window='right,75%,<80(up,75%,border-bottom)'", + "--preview='sel={}; less ${sel:-{q}} 2>/dev/null'", + ] + .into_iter() + .map(Into::into) + .collect(), } } } -#[derive(Debug, Args, Serialize, Deserialize)] -pub(crate) struct Directory { +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub(crate) struct Logging { + pub(crate) enabled: bool, pub(crate) path: PathBuf, - pub(crate) root: bool, - pub(crate) hidden: bool, -} - -#[derive(Debug, Default)] -pub(crate) struct Directories { - search_dirs: Vec, - root_dirs: Vec, } -impl From> for Directories { - fn from(value: Vec) -> Self { - value.iter().fold(Directories::default(), |mut acc, d| { - match d.root { - true => acc.root_dirs.push(d.path.to_owned()), - false => acc.search_dirs.push(d.path.to_owned()), - }; - acc - }) +impl Default for Logging { + fn default() -> Self { + Self { + enabled: Default::default(), + path: dirs::cache_dir() + .map(|p| p.join("tmuxr")) + .unwrap_or_default() + .join("tmuxr.log"), + } } } #[cfg(test)] mod tests { + use super::*; + use figment::providers::{Format, Serialized, Toml}; + + #[test] + fn defaults() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "tmuxr.toml", + r#" + [paths] + search = [] + add = [] + hidden = false + + [finder] + program = "fzf-tmux" + args = [ + "--", + "--multi", + "--print-query", + "-d/", + "--preview-window='right,75%,<80(up,75%,border-bottom)'", + "--preview='sel={}; less ${sel:-{q}} 2>/dev/null'", + ] + + [logging] + enabled = true + path = "/tmp/tmuxr/test/tmuxr.log" + "#, + )?; + + let config: Config = Figment::from(Serialized::defaults(Config::default())) + .merge(Toml::file("tmuxr.toml")) + .extract()?; + + assert_eq!(config, Config::default()); + + Ok(()) + }); + } + #[test] - fn test_start() { - assert_eq!(1, 1); + fn custom() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "tmuxr.toml", + r#" + [paths] + search = [ "/tmp/tmuxr/test/projects" ] + add = [ "/tmp/tmuxr/test/other_projects" ] + hidden = true + + [finder] + program = "fzf" + args = ["-0", "-1", "--preview='cat'"] + + [logging] + enabled = true + log_file = "/tmp/tmuxr/test/tmuxr.log" + "#, + )?; + + let config: Config = Figment::from(Serialized::defaults(Config::default())) + .merge(Toml::file("tmuxr.toml")) + .extract()?; + + assert_eq!( + config, + Config { + paths: Paths { + search: vec!["/tmp/tmuxr/test/projects".into()], + add: vec!["/tmp/tmuxr/test/extra_project".into()], + hidden: true, + }, + finder: Finder { + program: "fzf".into(), + args: vec!["-0".into(), "-1".into(), "--preview='cat'".into()], + }, + logging: Logging { + enabled: true, + path: "/tmp/tmuxr/test/tmuxr.log".into() + }, + } + ); + + Ok(()) + }); } } diff --git a/src/error.rs b/src/error.rs index e423493..5b0fea8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,4 +7,13 @@ pub enum Error { #[error("Config error: {0:?}")] Config(#[from] figment::error::Error), + + #[error("Ignore error: {0:?}")] + Ignore(#[from] ignore::Error), + + #[error("Process error: {0:?}")] + Finder(String), + + #[error("Path error: {0:?}")] + Path(String), } diff --git a/src/lib.rs b/src/lib.rs index 872ca50..acaa475 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,9 @@ +use std::{ + io::Write, + process::{Command, Stdio}, +}; +use tracing::info; + pub use crate::cli::Cli; pub use crate::config::Config; pub use crate::error::{Error, Result}; @@ -8,5 +14,21 @@ mod error; #[tracing::instrument()] pub fn run(config: &Config) -> Result<()> { + let mut finder = Command::new(&config.finder.program) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .args(&config.finder.args) + .spawn()?; + + finder + .stdin + .as_mut() + .ok_or_else(|| Error::Finder("Failed to get finder's stdin".to_string()))? + .write_all(config.paths.to_string().as_bytes())?; + + let output = finder.wait_with_output()?; + + info!("{:?}", output); + Ok(()) } diff --git a/src/main.rs b/src/main.rs index 40fccd6..c9b21fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ use clap::Parser; -use tmuxr::{Cli, Result}; +use tmuxr::{Cli, Config, Result}; use tracing_subscriber::prelude::*; fn main() -> Result<()> { let cli = Cli::parse(); - let config = cli.config.extract()?; + let config = Config::extract(cli.as_config())?; tracing_subscriber::registry().with(cli.as_layer()?).init(); -- cgit v1.2.3-70-g09d2