diff --git a/Cargo.lock b/Cargo.lock index 7c58ee0..5335674 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,17 +10,27 @@ dependencies = [ "env_logger", "evdev", "flate2", - "itertools", + "itertools 0.10.5", "log", "nix", "signal-hook", "signal-hook-tokio", + "sweet", "sysinfo", "tokio", "tokio-stream", "tokio-udev", ] +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -29,77 +39,99 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "bitflags" @@ -107,6 +139,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bitvec" version = "1.0.1" @@ -119,17 +157,26 @@ dependencies = [ "wyz", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bytes" -version = "1.3.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" [[package]] name = "cc" -version = "1.0.78" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" [[package]] name = "cfg-if" @@ -139,9 +186,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.1" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" dependencies = [ "clap_builder", "clap_derive", @@ -149,9 +196,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" dependencies = [ "anstream", "anstyle", @@ -161,91 +208,102 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] -name = "crc32fast" -version = "1.3.2" +name = "cpufeatures" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ - "cfg-if", + "libc", ] [[package]] -name = "crossbeam-channel" -version = "0.5.6" +name = "crc32fast" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", - "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset 0.7.1", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "cfg-if", + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", ] [[package]] name = "either" -version = "1.8.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "env_logger" @@ -262,9 +320,9 @@ dependencies = [ [[package]] name = "evdev" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bed59fcc8cfd6b190814a509018388462d3b203cf6dd10db5c00087e72a83f3" +checksum = "ab6055a93a963297befb0f4f6e18f314aec9767a4bbe88b151126df2433610a7" dependencies = [ "bitvec", "cfg-if", @@ -277,9 +335,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -293,15 +351,31 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -312,12 +386,24 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -327,11 +413,20 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "libc" -version = "0.2.138" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libudev-sys" @@ -345,9 +440,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -355,18 +450,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" @@ -377,34 +469,25 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.5" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi 0.3.9", "libc", - "log", "wasi", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -413,11 +496,11 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cc", "cfg-if", "libc", - "memoffset 0.6.5", + "memoffset", ] [[package]] @@ -430,26 +513,25 @@ dependencies = [ ] [[package]] -name = "num_cpus" -version = "1.14.0" +name = "object" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ - "hermit-abi", - "libc", + "memchr", ] [[package]] name = "once_cell" -version = "1.16.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -457,43 +539,88 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-targets", +] + +[[package]] +name = "pest" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +dependencies = [ + "once_cell", + "pest", + "sha2", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -506,9 +633,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rayon" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -516,30 +643,40 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.7.0" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -548,21 +685,38 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sha2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] [[package]] name = "signal-hook" -version = "0.3.14" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", @@ -570,9 +724,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -591,25 +745,39 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.4.7" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "winapi", + "windows-sys", ] [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "sweet" +version = "0.3.0" +source = "git+https://github.com/waycrate/sweet.git#a91d1ab6fb7988578ce359cfbf55e75b813d84db" +dependencies = [ + "anyhow", + "bitflags 2.6.0", + "evdev", + "itertools 0.12.1", + "pest", + "pest_derive", + "thiserror", +] [[package]] name = "swhks" @@ -624,20 +792,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.107" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -667,69 +824,67 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "termcolor" -version = "1.1.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn", ] [[package]] name = "tokio" -version = "1.24.2" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn", ] [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -747,6 +902,18 @@ dependencies = [ "udev", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "udev" version = "0.7.0" @@ -760,15 +927,21 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -794,11 +967,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys", ] [[package]] @@ -807,21 +980,6 @@ 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 0.42.0", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", - "windows_x86_64_gnullvm 0.42.0", - "windows_x86_64_msvc 0.42.0", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -833,102 +991,67 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_msvc" -version = "0.42.0" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "wyz" diff --git a/flake.lock b/flake.lock index 900ef5a..b80c063 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1687977148, - "narHash": "sha256-gUcXiU2GgjYIc65GOIemdBJZ+lkQxuyIh7OkR9j0gCo=", + "lastModified": 1715499532, + "narHash": "sha256-9UJLb8rdi2VokYcfOBQHUzP3iNxOPNWcbK++ENElpk0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "60a783e00517fce85c42c8c53fe0ed05ded5b2a4", + "rev": "af8b9db5c00f1a8e4b83578acc578ff7d823b786", "type": "github" }, "original": { diff --git a/swhkd/Cargo.toml b/swhkd/Cargo.toml index d9fc31e..14ef242 100644 --- a/swhkd/Cargo.toml +++ b/swhkd/Cargo.toml @@ -21,6 +21,7 @@ log = "0.4.14" nix = "0.23.1" signal-hook = "0.3.13" signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] } +sweet = { git = "https://github.com/waycrate/sweet.git", version = "0.3.0" } sysinfo = "0.23.5" tokio = { version = "1.24.2", features = ["full"] } tokio-stream = "0.1.8" diff --git a/swhkd/src/config.rs b/swhkd/src/config.rs index 1923a58..d64517f 100644 --- a/swhkd/src/config.rs +++ b/swhkd/src/config.rs @@ -1,147 +1,11 @@ -use itertools::Itertools; -use std::collections::HashMap; -use std::fs; -use std::{ - fmt, - path::{Path, PathBuf}, -}; +use std::path::Path; +use sweet::KeyAttribute; +use sweet::{Definition, SwhkdParser}; +use sweet::{ModeInstruction, ParseError}; -#[derive(Debug)] -pub enum Error { - ConfigNotFound, - Io(std::io::Error), - InvalidConfig(ParseError), -} - -#[derive(Debug, PartialEq, Eq)] -pub enum ParseError { - // u32 is the line number where an error occured - UnknownSymbol(PathBuf, u32), - InvalidModifier(PathBuf, u32), - InvalidKeysym(PathBuf, u32), -} - -impl From for Error { - fn from(val: std::io::Error) -> Self { - if val.kind() == std::io::ErrorKind::NotFound { - Error::ConfigNotFound - } else { - Error::Io(val) - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::ConfigNotFound => "Config file not found.".fmt(f), - - Error::Io(io_err) => format!("I/O Error while parsing config file: {}", io_err).fmt(f), - Error::InvalidConfig(parse_err) => match parse_err { - ParseError::UnknownSymbol(path, line_nr) => format!( - "Error parsing config file {:?}. Unknown symbol at line {}.", - path, line_nr - ) - .fmt(f), - ParseError::InvalidKeysym(path, line_nr) => format!( - "Error parsing config file {:?}. Invalid keysym at line {}.", - path, line_nr - ) - .fmt(f), - ParseError::InvalidModifier(path, line_nr) => format!( - "Error parsing config file {:?}. Invalid modifier at line {}.", - path, line_nr - ) - .fmt(f), - }, - } - } -} - -pub const IMPORT_STATEMENT: &str = "include"; -pub const UNBIND_STATEMENT: &str = "ignore"; -pub const MODE_STATEMENT: &str = "mode"; -pub const MODE_END_STATEMENT: &str = "endmode"; -pub const MODE_ENTER_STATEMENT: &str = "@enter"; -pub const MODE_ESCAPE_STATEMENT: &str = "@escape"; -pub const MODE_SWALLOW_STATEMENT: &str = "swallow"; -pub const MODE_ONEOFF_STATEMENT: &str = "oneoff"; - -#[derive(Debug, PartialEq, Clone, Eq)] -pub struct Config { - pub path: PathBuf, - pub contents: String, - pub imports: Vec, -} - -impl Config { - pub fn get_imports(contents: &str) -> Result, Error> { - let mut imports = Vec::new(); - for line in contents.lines() { - if line.split(' ').next().unwrap() == IMPORT_STATEMENT { - if let Some(import_path) = line.split(' ').nth(1) { - imports.push(Path::new(import_path).to_path_buf()); - } - } - } - Ok(imports) - } - - pub fn new(path: &Path) -> Result { - let contents = fs::read_to_string(path)?; - let imports = Self::get_imports(&contents)?; - Ok(Config { path: path.to_path_buf(), contents, imports }) - } - - pub fn load_to_configs(&self) -> Result, Error> { - let mut configs = Vec::new(); - for import in &self.imports { - configs.push(Self::new(import)?) - } - Ok(configs) - } - - pub fn load_and_merge(config: Self) -> Result, Error> { - let mut configs = vec![config]; - let mut prev_count = 0; - let mut current_count = configs.len(); - while prev_count != current_count { - prev_count = configs.len(); - for config in configs.clone() { - for import in Self::load_to_configs(&config)? { - if !configs.contains(&import) { - configs.push(import); - } - } - } - current_count = configs.len(); - } - Ok(configs) - } -} - -pub fn load(path: &Path) -> Result, Error> { - let config_self = Config::new(path)?; - let mut configs: Vec = Config::load_and_merge(config_self.clone())?; - configs.remove(0); - configs.push(config_self); - let mut modes: Vec = vec![Mode::default()]; - for config in configs { - let mut output = parse_contents(path.to_path_buf(), config.contents)?; - for hotkey in output[0].hotkeys.drain(..) { - modes[0].hotkeys.retain(|hk| hk.keybinding != hotkey.keybinding); - modes[0].hotkeys.push(hotkey); - } - for unbind in output[0].unbinds.drain(..) { - modes[0].hotkeys.retain(|hk| hk.keybinding != unbind); - } - output.remove(0); - for mut mode in output { - mode.hotkeys.retain(|x| !mode.unbinds.contains(&x.keybinding)); - modes.push(mode); - } - } - Ok(modes) +pub fn load(path: &Path) -> Result, ParseError> { + let config_self = sweet::SwhkdParser::from(sweet::ParserInput::Path(path)).unwrap(); + parse_contents(config_self) } #[derive(Debug, Clone)] @@ -214,6 +78,7 @@ impl Value for KeyBinding { pub struct Hotkey { pub keybinding: KeyBinding, pub command: String, + pub mode_instructions: Vec, } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -228,11 +93,15 @@ pub enum Modifier { impl Hotkey { pub fn from_keybinding(keybinding: KeyBinding, command: String) -> Self { - Hotkey { keybinding, command } + Hotkey { keybinding, command, mode_instructions: vec![] } } #[cfg(test)] pub fn new(keysym: evdev::Key, modifiers: Vec, command: String) -> Self { - Hotkey { keybinding: KeyBinding::new(keysym, modifiers), command } + Hotkey { + keybinding: KeyBinding::new(keysym, modifiers), + command, + mode_instructions: vec![], + } } } @@ -270,511 +139,76 @@ pub struct Mode { pub options: ModeOptions, } +impl Default for Mode { + fn default() -> Self { + Self { + name: "normal".to_string(), + hotkeys: vec![], + unbinds: vec![], + options: ModeOptions::default(), + } + } +} + #[derive(Debug, Clone, PartialEq, Default)] pub struct ModeOptions { pub swallow: bool, pub oneoff: bool, } -impl Mode { - pub fn new(name: String) -> Self { - Self { name, hotkeys: Vec::new(), unbinds: Vec::new(), options: ModeOptions::default() } - } +pub fn parse_contents(contents: SwhkdParser) -> Result, ParseError> { + let mut default_mode = Mode::default(); - pub fn default() -> Self { - Self::new("normal".to_string()) + for binding in &contents.bindings { + default_mode.hotkeys.push(Hotkey { + keybinding: sweet_def_to_kb(&binding.definition), + command: binding.command.clone(), + mode_instructions: binding.mode_instructions.clone(), + }); } -} - -pub fn parse_contents(path: PathBuf, contents: String) -> Result, Error> { - // Don't forget to update valid key list on the man page if you do change this list. - let key_to_evdev_key: HashMap<&str, evdev::Key> = HashMap::from([ - ("q", evdev::Key::KEY_Q), - ("w", evdev::Key::KEY_W), - ("e", evdev::Key::KEY_E), - ("r", evdev::Key::KEY_R), - ("t", evdev::Key::KEY_T), - ("y", evdev::Key::KEY_Y), - ("u", evdev::Key::KEY_U), - ("i", evdev::Key::KEY_I), - ("o", evdev::Key::KEY_O), - ("p", evdev::Key::KEY_P), - ("a", evdev::Key::KEY_A), - ("s", evdev::Key::KEY_S), - ("d", evdev::Key::KEY_D), - ("f", evdev::Key::KEY_F), - ("g", evdev::Key::KEY_G), - ("h", evdev::Key::KEY_H), - ("j", evdev::Key::KEY_J), - ("k", evdev::Key::KEY_K), - ("l", evdev::Key::KEY_L), - ("z", evdev::Key::KEY_Z), - ("x", evdev::Key::KEY_X), - ("c", evdev::Key::KEY_C), - ("v", evdev::Key::KEY_V), - ("b", evdev::Key::KEY_B), - ("n", evdev::Key::KEY_N), - ("m", evdev::Key::KEY_M), - ("1", evdev::Key::KEY_1), - ("2", evdev::Key::KEY_2), - ("3", evdev::Key::KEY_3), - ("4", evdev::Key::KEY_4), - ("5", evdev::Key::KEY_5), - ("6", evdev::Key::KEY_6), - ("7", evdev::Key::KEY_7), - ("8", evdev::Key::KEY_8), - ("9", evdev::Key::KEY_9), - ("0", evdev::Key::KEY_0), - ("escape", evdev::Key::KEY_ESC), - ("backspace", evdev::Key::KEY_BACKSPACE), - ("capslock", evdev::Key::KEY_CAPSLOCK), - ("return", evdev::Key::KEY_ENTER), - ("enter", evdev::Key::KEY_ENTER), - ("tab", evdev::Key::KEY_TAB), - ("space", evdev::Key::KEY_SPACE), - ("plus", evdev::Key::KEY_KPPLUS), // Shouldn't this be kpplus? - ("kp0", evdev::Key::KEY_KP0), - ("kp1", evdev::Key::KEY_KP1), - ("kp2", evdev::Key::KEY_KP2), - ("kp3", evdev::Key::KEY_KP3), - ("kp4", evdev::Key::KEY_KP4), - ("kp5", evdev::Key::KEY_KP5), - ("kp6", evdev::Key::KEY_KP6), - ("kp7", evdev::Key::KEY_KP7), - ("kp8", evdev::Key::KEY_KP8), - ("kp9", evdev::Key::KEY_KP9), - ("kpasterisk", evdev::Key::KEY_KPASTERISK), - ("kpcomma", evdev::Key::KEY_KPCOMMA), - ("kpdot", evdev::Key::KEY_KPDOT), - ("kpenter", evdev::Key::KEY_KPENTER), - ("kpequal", evdev::Key::KEY_KPEQUAL), - ("kpjpcomma", evdev::Key::KEY_KPJPCOMMA), - ("kpleftparen", evdev::Key::KEY_KPLEFTPAREN), - ("kpminus", evdev::Key::KEY_KPMINUS), - ("kpplusminus", evdev::Key::KEY_KPPLUSMINUS), - ("kprightparen", evdev::Key::KEY_KPRIGHTPAREN), - ("minus", evdev::Key::KEY_MINUS), - ("-", evdev::Key::KEY_MINUS), - ("equal", evdev::Key::KEY_EQUAL), - ("=", evdev::Key::KEY_EQUAL), - ("grave", evdev::Key::KEY_GRAVE), - ("`", evdev::Key::KEY_GRAVE), - ("print", evdev::Key::KEY_SYSRQ), - ("volumeup", evdev::Key::KEY_VOLUMEUP), - ("xf86audioraisevolume", evdev::Key::KEY_VOLUMEUP), - ("volumedown", evdev::Key::KEY_VOLUMEDOWN), - ("xf86audiolowervolume", evdev::Key::KEY_VOLUMEDOWN), - ("mute", evdev::Key::KEY_MUTE), - ("xf86audiomute", evdev::Key::KEY_MUTE), - ("brightnessup", evdev::Key::KEY_BRIGHTNESSUP), - ("xf86monbrightnessup", evdev::Key::KEY_BRIGHTNESSUP), - ("brightnessdown", evdev::Key::KEY_BRIGHTNESSDOWN), - ("xf86audiomedia", evdev::Key::KEY_MEDIA), - ("xf86audiomicmute", evdev::Key::KEY_MICMUTE), - ("micmute", evdev::Key::KEY_MICMUTE), - ("xf86audionext", evdev::Key::KEY_NEXTSONG), - ("xf86audioplay", evdev::Key::KEY_PLAYPAUSE), - ("xf86audioprev", evdev::Key::KEY_PREVIOUSSONG), - ("xf86audiostop", evdev::Key::KEY_STOP), - ("xf86monbrightnessdown", evdev::Key::KEY_BRIGHTNESSDOWN), - (",", evdev::Key::KEY_COMMA), - ("comma", evdev::Key::KEY_COMMA), - (".", evdev::Key::KEY_DOT), - ("dot", evdev::Key::KEY_DOT), - ("period", evdev::Key::KEY_DOT), - ("/", evdev::Key::KEY_SLASH), - ("question", evdev::Key::KEY_QUESTION), - ("slash", evdev::Key::KEY_SLASH), - ("backslash", evdev::Key::KEY_BACKSLASH), - ("leftbrace", evdev::Key::KEY_LEFTBRACE), - ("[", evdev::Key::KEY_LEFTBRACE), - ("bracketleft", evdev::Key::KEY_LEFTBRACE), - ("rightbrace", evdev::Key::KEY_RIGHTBRACE), - ("]", evdev::Key::KEY_RIGHTBRACE), - ("bracketright", evdev::Key::KEY_RIGHTBRACE), - (";", evdev::Key::KEY_SEMICOLON), - ("scroll_lock", evdev::Key::KEY_SCROLLLOCK), - ("semicolon", evdev::Key::KEY_SEMICOLON), - ("'", evdev::Key::KEY_APOSTROPHE), - ("apostrophe", evdev::Key::KEY_APOSTROPHE), - ("left", evdev::Key::KEY_LEFT), - ("right", evdev::Key::KEY_RIGHT), - ("up", evdev::Key::KEY_UP), - ("down", evdev::Key::KEY_DOWN), - ("pause", evdev::Key::KEY_PAUSE), - ("home", evdev::Key::KEY_HOME), - ("delete", evdev::Key::KEY_DELETE), - ("insert", evdev::Key::KEY_INSERT), - ("end", evdev::Key::KEY_END), - ("pause", evdev::Key::KEY_PAUSE), - ("prior", evdev::Key::KEY_PAGEDOWN), - ("next", evdev::Key::KEY_PAGEUP), - ("pagedown", evdev::Key::KEY_PAGEDOWN), - ("pageup", evdev::Key::KEY_PAGEUP), - ("f1", evdev::Key::KEY_F1), - ("f2", evdev::Key::KEY_F2), - ("f3", evdev::Key::KEY_F3), - ("f4", evdev::Key::KEY_F4), - ("f5", evdev::Key::KEY_F5), - ("f6", evdev::Key::KEY_F6), - ("f7", evdev::Key::KEY_F7), - ("f8", evdev::Key::KEY_F8), - ("f9", evdev::Key::KEY_F9), - ("f10", evdev::Key::KEY_F10), - ("f11", evdev::Key::KEY_F11), - ("f12", evdev::Key::KEY_F12), - ("f13", evdev::Key::KEY_F13), - ("f14", evdev::Key::KEY_F14), - ("f15", evdev::Key::KEY_F15), - ("f16", evdev::Key::KEY_F16), - ("f17", evdev::Key::KEY_F17), - ("f18", evdev::Key::KEY_F18), - ("f19", evdev::Key::KEY_F19), - ("f20", evdev::Key::KEY_F20), - ("f21", evdev::Key::KEY_F21), - ("f22", evdev::Key::KEY_F22), - ("f23", evdev::Key::KEY_F23), - ("f24", evdev::Key::KEY_F24), - ]); - - // Don't forget to update modifier list on the man page if you do change this list. - let mod_to_mod_enum: HashMap<&str, Modifier> = HashMap::from([ - ("ctrl", Modifier::Control), - ("control", Modifier::Control), - ("super", Modifier::Super), - ("mod4", Modifier::Super), - ("alt", Modifier::Alt), - ("mod1", Modifier::Alt), - ("altgr", Modifier::Altgr), - ("mod5", Modifier::Altgr), - ("shift", Modifier::Shift), - ("any", Modifier::Any), - ]); - - let lines: Vec<&str> = contents.split('\n').collect(); - let mut modes: Vec = vec![Mode::default()]; - let mut current_mode: usize = 0; - - // Go through each line, ignore comments and empty lines, mark lines starting with whitespace - // as commands, and mark the other lines as keysyms. Mark means storing a line's type and the - // line number in a vector. - let mut lines_with_types: Vec<(&str, u32)> = Vec::new(); - for (line_number, line) in lines.iter().enumerate() { - if line.trim().starts_with('#') - || line.split(' ').next().unwrap() == IMPORT_STATEMENT - || line.trim().is_empty() - { - continue; - } - if line.starts_with(' ') || line.starts_with('\t') { - lines_with_types.push(("command", line_number as u32)); - } else if line.starts_with(UNBIND_STATEMENT) { - lines_with_types.push(("unbind", line_number as u32)); - } else if line.starts_with(MODE_STATEMENT) { - lines_with_types.push(("modestart", line_number as u32)); - } else if line.starts_with(MODE_END_STATEMENT) { - lines_with_types.push(("modeend", line_number as u32)); - } else { - lines_with_types.push(("keysym", line_number as u32)); - } - } - - // Edge case: return a blank vector if no lines detected - if lines_with_types.is_empty() { - return Ok(modes); - } - - let mut actual_lines: Vec<(&str, u32, String)> = Vec::new(); - - if contents.contains('\\') { - // Go through lines_with_types, and add the next line over and over until the current line no - // longer ends with backslash. (Only if the lines have the same type) - let mut current_line_type = lines_with_types[0].0; - let mut current_line_number = lines_with_types[0].1; - let mut current_line_string = String::new(); - let mut continue_backslash; - - for (line_type, line_number) in lines_with_types { - if line_type != current_line_type { - current_line_type = line_type; - current_line_number = line_number; - current_line_string = String::new(); - } - - let line_to_add = lines[line_number as usize].trim(); - continue_backslash = line_to_add.ends_with('\\'); - - let line_to_add = line_to_add.strip_suffix('\\').unwrap_or(line_to_add); - - current_line_string.push_str(line_to_add); - - if !continue_backslash { - actual_lines.push((current_line_type, current_line_number, current_line_string)); - current_line_type = line_type; - current_line_number = line_number; - current_line_string = String::new(); - } - } - } else { - for (line_type, line_number) in lines_with_types { - actual_lines.push(( - line_type, - line_number, - lines[line_number as usize].trim().to_string(), - )); - } + for unbind in contents.unbinds { + default_mode.unbinds.push(sweet_def_to_kb(&unbind)); } - drop(lines); + let mut modes = vec![default_mode]; - for (i, item) in actual_lines.iter().enumerate() { - let line_type = item.0; - let line_number = item.1; - let line = &item.2; - - if line_type == "unbind" { - let to_unbind = line.trim_start_matches(UNBIND_STATEMENT).trim(); - modes[current_mode].unbinds.push(parse_keybind( - path.clone(), - to_unbind, - line_number + 1, - &key_to_evdev_key, - &mod_to_mod_enum, - )?); - } - - if line_type == "modestart" { - let tokens = line.split(' ').collect_vec(); - let modename = tokens[1]; - let mut mode = Mode::new(modename.to_string()); - mode.options.swallow = tokens.contains(&MODE_SWALLOW_STATEMENT); - mode.options.oneoff = tokens.contains(&MODE_ONEOFF_STATEMENT); - modes.push(mode); - current_mode = modes.len() - 1; - } - - if line_type == "modeend" { - current_mode = 0; - } - - if line_type != "keysym" { - continue; - } - - let next_line = actual_lines.get(i + 1); - if next_line.is_none() { - break; - } - let next_line = next_line.unwrap(); - - if next_line.0 != "command" { - continue; // this should ignore keysyms that are not followed by a command + for sweet::Mode { name, oneoff, swallow, bindings, unbinds } in contents.modes { + let mut pushmode = + Mode { name, options: ModeOptions { swallow, oneoff }, ..Default::default() }; + for binding in bindings { + let hotkey = Hotkey { + keybinding: sweet_def_to_kb(&binding.definition), + command: binding.command, + mode_instructions: binding.mode_instructions.clone(), + }; + pushmode.hotkeys.retain(|h| h.keybinding != hotkey.keybinding); + pushmode.hotkeys.push(hotkey); } - - let extracted_keys = extract_curly_brace(line); - let extracted_commands = extract_curly_brace(&next_line.2); - - for (key, command) in extracted_keys.iter().zip(extracted_commands.iter()) { - let keybinding = parse_keybind( - path.clone(), - key, - line_number + 1, - &key_to_evdev_key, - &mod_to_mod_enum, - )?; - let hotkey = Hotkey::from_keybinding(keybinding, command.to_string()); - - // Override latter - modes[current_mode].hotkeys.retain(|h| h.keybinding != hotkey.keybinding); - modes[current_mode].hotkeys.push(hotkey); + for unbind in unbinds { + pushmode.unbinds.push(sweet_def_to_kb(&unbind)); } + modes.push(pushmode); } - Ok(modes) } -// We need to get the reference to key_to_evdev_key -// and mod_to_mod enum instead of recreating them -// after each function call because it's too expensive -fn parse_keybind( - path: PathBuf, - line: &str, - line_nr: u32, - key_to_evdev_key: &HashMap<&str, evdev::Key>, - mod_to_mod_enum: &HashMap<&str, Modifier>, -) -> Result { - let line = line.split('#').next().unwrap(); - let tokens: Vec = - line.split('+').map(|s| s.trim().to_lowercase()).filter(|s| s != "_").collect(); - - let mut tokens_new = Vec::new(); - for mut token in tokens { - while token.trim().starts_with('_') { - token = token.trim().strip_prefix('_').unwrap().to_string(); - } - tokens_new.push(token.trim().to_string()); - } - - let last_token = tokens_new.last().unwrap().trim(); - - // Check if last_token is prefixed with @ or ~ or even both. - // If prefixed @, on_release = true; if prefixed ~, send = true - let send = last_token.starts_with('~') || last_token.starts_with("@~"); - let on_release = last_token.starts_with('@') || last_token.starts_with("~@"); - - // Delete the @ and ~ in the last token - fn strip_at(token: &str) -> &str { - token.trim_start_matches(['@', '~']) - } - - let last_token = strip_at(last_token); - let tokens_no_at: Vec<_> = tokens_new.iter().map(|token| strip_at(token)).collect(); - - // Check if each token is valid - for token in &tokens_no_at { - if key_to_evdev_key.contains_key(token) { - // Can't have a keysym that's like a modifier - if *token != last_token { - return Err(Error::InvalidConfig(ParseError::InvalidModifier(path, line_nr))); - } - } else if mod_to_mod_enum.contains_key(token) { - // Can't have a modifier that's like a keysym - if *token == last_token { - return Err(Error::InvalidConfig(ParseError::InvalidKeysym(path, line_nr))); - } - } else { - return Err(Error::InvalidConfig(ParseError::UnknownSymbol(path, line_nr))); - } - } - - // Translate keypress into evdev key - let keysym = key_to_evdev_key.get(last_token).unwrap(); - - let modifiers: Vec = tokens_no_at[0..(tokens_no_at.len() - 1)] +/// A small function to convert a `sweet::Modifier` into the local `Modifier` enum +fn sweet_def_to_kb(def: &Definition) -> KeyBinding { + let modifiers = def + .modifiers .iter() - .map(|token| *mod_to_mod_enum.get(token).unwrap()) + .filter_map(|m| match m { + sweet::Modifier::Super => Some(Modifier::Super), + sweet::Modifier::Any => Some(Modifier::Any), + sweet::Modifier::Control => Some(Modifier::Control), + sweet::Modifier::Alt => Some(Modifier::Alt), + sweet::Modifier::Altgr => Some(Modifier::Altgr), + sweet::Modifier::Shift => Some(Modifier::Shift), + sweet::Modifier::Omission => None, + }) .collect(); - let mut keybinding = KeyBinding::new(*keysym, modifiers); - if send { - keybinding = keybinding.send(); - } - if on_release { - keybinding = keybinding.on_release(); - } - Ok(keybinding) -} - -pub fn extract_curly_brace(line: &str) -> Vec { - if !line.contains('{') || !line.contains('}') || !line.is_ascii() { - return vec![line.to_string()]; - } - - // go through each character in the line and mark the position of each { and } - // if a { is not followed by a }, return the line as is - let mut brace_positions: Vec = Vec::new(); - let mut flag = false; - for (i, c) in line.chars().enumerate() { - if c == '{' { - if flag { - return vec![line.to_string()]; - } - brace_positions.push(i); - flag = true; - } else if c == '}' { - if !flag { - return vec![line.to_string()]; - } - brace_positions.push(i); - flag = false; - } - } - - // now we have a list of positions of { and } - // we should extract the items between each pair of braces and store them in a vector - let mut items: Vec = Vec::new(); - let mut remaining_line: Vec = Vec::new(); - let mut start_index = 0; - for i in brace_positions.chunks(2) { - items.push(line[i[0] + 1..i[1]].to_string()); - remaining_line.push(line[start_index..i[0]].to_string()); - start_index = i[1] + 1; - } - - // now we have a list of items between each pair of braces - // we should extract the items between each comma and store them in a vector - let mut tokens_vec: Vec> = Vec::new(); - for item in items { - // Edge case: escape periods - // example: - // ``` - // super + {\,, .} - // riverctl focus-output {previous, next} - // ``` - let item = item.replace("\\,", "comma"); - - let items: Vec = item.split(',').map(|s| s.trim().to_string()).collect(); - tokens_vec.push(handle_ranges(items)); - } - - fn handle_ranges(items: Vec) -> Vec { - let mut output: Vec = Vec::new(); - for item in items { - if !item.contains('-') { - output.push(item); - continue; - } - let mut range = item.split('-').map(|s| s.trim()); - - let begin_char: &str = if let Some(b) = range.next() { - b - } else { - output.push(item); - continue; - }; - - let end_char: &str = if let Some(e) = range.next() { - e - } else { - output.push(item); - continue; - }; - - // Do not accept range values that are longer than one char - // Example invalid: {ef-p} {3-56} - // Beginning of the range cannot be greater than end - // Example invalid: {9-4} {3-2} - if begin_char.len() != 1 || end_char.len() != 1 || begin_char > end_char { - output.push(item); - continue; - } - - // In swhkd we will parse the full range using ASCII values. - - let begin_ascii_val = begin_char.parse::().unwrap() as u8; - let end_ascii_val = end_char.parse::().unwrap() as u8; - - for ascii_number in begin_ascii_val..=end_ascii_val { - output.push((ascii_number as char).to_string()); - } - } - output - } - - // now write the tokens back to the line and output a vector - let mut output: Vec = Vec::new(); - // generate a cartesian product iterator for all the vectors in tokens_vec - let cartesian_product_iter = tokens_vec.iter().multi_cartesian_product(); - for tokens in cartesian_product_iter.collect_vec() { - let mut line_to_push = String::new(); - for i in 0..remaining_line.len() { - line_to_push.push_str(&remaining_line[i]); - line_to_push.push_str(tokens[i]); - } - if brace_positions[brace_positions.len() - 1] < line.len() - 1 { - line_to_push.push_str(&line[brace_positions[brace_positions.len() - 1] + 1..]); - } - output.push(line_to_push); - } - output + let send = def.key.attribute == KeyAttribute::Send; + let on_release = def.key.attribute == KeyAttribute::OnRelease; + KeyBinding { keysym: def.key.key, modifiers, send, on_release } } diff --git a/swhkd/src/daemon.rs b/swhkd/src/daemon.rs index 4c67f36..d155b3c 100644 --- a/swhkd/src/daemon.rs +++ b/swhkd/src/daemon.rs @@ -31,9 +31,6 @@ mod environ; mod perms; mod uinput; -#[cfg(test)] -mod tests; - struct KeyboardState { state_modifiers: HashSet, state_keysyms: AttributeSet, @@ -502,38 +499,23 @@ pub fn send_command( ) { log::info!("Hotkey pressed: {:#?}", hotkey); let command = hotkey.command; - let mut commands_to_send = String::new(); - if modes[mode_stack[mode_stack.len() - 1]].options.oneoff { + if modes[*mode_stack.last().unwrap()].options.oneoff { mode_stack.pop(); } - if command.contains('@') { - let commands = command.split("&&").map(|s| s.trim()).collect::>(); - for cmd in commands { - let mut words = cmd.split_whitespace(); - match words.next().unwrap() { - config::MODE_ENTER_STATEMENT => { - let enter_mode = cmd.split(' ').nth(1).unwrap(); - for (i, mode) in modes.iter().enumerate() { - if mode.name == enter_mode { - mode_stack.push(i); - break; - } - } - log::info!("Entering mode: {}", modes[mode_stack[mode_stack.len() - 1]].name); + for mode in hotkey.mode_instructions.iter() { + match mode { + sweet::ModeInstruction::Enter(name) => { + if let Some(mode_index) = modes.iter().position(|modename| modename.name.eq(name)) { + mode_stack.push(mode_index); + log::info!("Entering mode: {}", name); } - config::MODE_ESCAPE_STATEMENT => { - mode_stack.pop(); - } - _ => commands_to_send.push_str(format!("{cmd} &&").as_str()), + } + sweet::ModeInstruction::Escape => { + mode_stack.pop(); } } - } else { - commands_to_send = command; - } - if commands_to_send.ends_with(" &&") { - commands_to_send = commands_to_send.strip_suffix(" &&").unwrap().to_string(); } - if let Err(e) = socket_write(&commands_to_send, socket_path.to_path_buf()) { + if let Err(e) = socket_write(&command, socket_path.to_path_buf()) { log::error!("Failed to send command to swhks through IPC."); log::error!("Please make sure that swhks is running."); log::error!("Err: {:#?}", e) diff --git a/swhkd/src/tests.rs b/swhkd/src/tests.rs deleted file mode 100644 index fe7d93a..0000000 --- a/swhkd/src/tests.rs +++ /dev/null @@ -1,1231 +0,0 @@ -mod test_config { - use crate::config::{ - extract_curly_brace, load, parse_contents, Error, Hotkey, Modifier, ParseError, Prefix, - }; - use std::fs; - use std::io::Write; - use std::{fs::File, path::PathBuf}; - - // Implement a struct for a path used in tests - // so that the test file will be automatically removed - // no matter how the test goes - struct TestPath { - path: PathBuf, - } - - impl TestPath { - fn new(path: &str) -> Self { - TestPath { path: PathBuf::from(path) } - } - - // Create a path method for a more succinct way - // to deal with borrowing the path value - fn path(&self) -> PathBuf { - self.path.clone() - } - } - - impl Drop for TestPath { - fn drop(self: &mut TestPath) { - if self.path.exists() { - fs::remove_file(self.path()).unwrap(); - } - } - } - - // Wrapper for config tests - fn eval_config_test(contents: &str, expected_hotkeys: Vec) -> std::io::Result<()> { - let result = parse_contents(PathBuf::new(), contents.to_string()); - - let mut expected_hotkeys_mut = expected_hotkeys; - - if result.is_err() { - panic!("Expected Ok config, found Err {:?}", result.unwrap_err()); - } - - let result = &result.unwrap()[0]; - let actual_hotkeys = &result.hotkeys; - - assert_eq!(actual_hotkeys.len(), expected_hotkeys_mut.len()); - - // Go through each actual hotkey, and pop a corresponding - // hotkey from the expected hotkeys - // to make sure that order does not matter - for hotkey in actual_hotkeys { - if let Some(index) = expected_hotkeys_mut.iter().position(|key| { - key.keybinding == hotkey.keybinding && key.command == hotkey.command - }) { - expected_hotkeys_mut.remove(index); - } else { - panic!( - "unexpected hotkey {:#?} found in result\nExpected result:\n{:#?}", - hotkey, expected_hotkeys_mut - ); - } - } - - if !expected_hotkeys_mut.is_empty() { - panic!( - "Some hotkeys were not returned by the actual result:\n{:#?}", - expected_hotkeys_mut - ); - } - - Ok(()) - } - - // Wrapper for the many error tests - fn eval_invalid_config_test( - contents: &str, - parse_error_type: ParseError, - ) -> std::io::Result<()> { - let result = parse_contents(PathBuf::new(), contents.to_string()); - - assert!(result.is_err()); - let result = result.unwrap_err(); - - // Check if the Error type is InvalidConfig - let result = match result { - Error::InvalidConfig(parse_err) => parse_err, - _ => panic!(), - }; - - // Check the ParseError enum type - if result != parse_error_type { - panic!("ParseError: Expected `{:?}`, found `{:?}`", parse_error_type, result); - } - - Ok(()) - } - - #[test] - fn test_existing_file() -> std::io::Result<()> { - let setup = TestPath::new("/tmp/swhkd-test-file1"); - // Build a dummy file in /tmp - let mut f = File::create(setup.path())?; - f.write_all( - b" -x - dmenu_run - -q - bspc node -q", - )?; - - let result = fs::read_to_string(&setup.path()); - assert!(result.is_ok()); - Ok(()) - } - - #[test] - fn test_load_multiple_config() -> std::io::Result<()> { - let setup = TestPath::new("/tmp/swhkd-test-file2"); - let mut f = File::create(setup.path())?; - f.write_all( - b" -include /tmp/swhkd-test-file3 -super + b - firefox", - )?; - - let setup2 = TestPath::new("/tmp/swhkd-test-file3"); - let mut f2 = File::create(setup2.path())?; - f2.write_all( - b" -super + c - hello", - )?; - - let hotkeys = &load(&setup.path()).unwrap()[0].hotkeys; - assert_eq!( - *hotkeys, - vec!( - Hotkey::new(evdev::Key::KEY_C, vec![Modifier::Super], String::from("hello")), - Hotkey::new(evdev::Key::KEY_B, vec![Modifier::Super], String::from("firefox")) - ) - ); - Ok(()) - } - - #[test] - fn test_relative_import() -> std::io::Result<()> { - let setup = TestPath::new("/tmp/swhkd-relative-file1"); - let mut f = File::create(setup.path())?; - f.write_all( - b" -include swhkd-relative-file2 -super + b - firefox", - )?; - - let setup2 = TestPath::new("swhkd-relative-file2"); - let mut f2 = File::create(setup2.path())?; - f2.write_all( - b" -super + c - hello", - )?; - - let hotkeys = &load(&setup.path()).unwrap()[0].hotkeys; - assert_eq!( - *hotkeys, - vec!( - Hotkey::new(evdev::Key::KEY_C, vec![Modifier::Super], String::from("hello")), - Hotkey::new(evdev::Key::KEY_B, vec![Modifier::Super], String::from("firefox")) - ) - ); - Ok(()) - } - - #[test] - fn test_more_multiple_configs() -> std::io::Result<()> { - let setup = TestPath::new("/tmp/swhkd-test-file4"); - let mut f = File::create(setup.path())?; - f.write_all( - b" -a - a", - )?; - - let setup2 = TestPath::new("/tmp/swhkd-test-file5"); - let mut f2 = File::create(setup2.path())?; - f2.write_all( - b" -include /tmp/swhkd-test-file4 -b - b", - )?; - let setup3 = TestPath::new("/tmp/swhkd-test-file6"); - let mut f3 = File::create(setup3.path())?; - f3.write_all( - b" -include /tmp/swhkd-test-file4 -include /tmp/swhkd-test-file5 -include /tmp/swhkd-test-file6 -include /tmp/swhkd-test-file7 -c - c", - )?; - let setup4 = TestPath::new("/tmp/swhkd-test-file7"); - let mut f4 = File::create(setup4.path())?; - f4.write_all( - b" -include /tmp/swhkd-test-file6 -d - d", - )?; - - let hotkeys = &load(&setup4.path()).unwrap()[0].hotkeys; - assert_eq!( - *hotkeys, - vec!( - Hotkey::new(evdev::Key::KEY_C, vec![], String::from("c")), - Hotkey::new(evdev::Key::KEY_A, vec![], String::from("a")), - Hotkey::new(evdev::Key::KEY_B, vec![], String::from("b")), - Hotkey::new(evdev::Key::KEY_D, vec![], String::from("d")), - ) - ); - Ok(()) - } - #[test] - fn test_include_and_unbind() -> std::io::Result<()> { - let setup = TestPath::new("/tmp/swhkd-test-file8"); - let mut f = File::create(setup.path())?; - f.write_all( - b" -include /tmp/swhkd-test-file9 -super + b - firefox -ignore super + d", - )?; - - let setup2 = TestPath::new("/tmp/swhkd-test-file9"); - let mut f2 = File::create(setup2.path())?; - f2.write_all( - b" -super + c - hello -super + d - world", - )?; - - let hotkeys = &load(&setup.path()).unwrap()[0].hotkeys; - assert_eq!( - *hotkeys, - vec!( - Hotkey::new(evdev::Key::KEY_C, vec![Modifier::Super], String::from("hello")), - Hotkey::new(evdev::Key::KEY_B, vec![Modifier::Super], String::from("firefox")) - ) - ); - Ok(()) - } - - #[test] - fn test_basic_keybind() -> std::io::Result<()> { - let contents = " -r - alacritty - "; - - eval_config_test( - contents, - vec![Hotkey::new(evdev::Key::KEY_R, vec![], String::from("alacritty"))], - ) - } - - #[test] - fn test_multiple_keybinds() -> std::io::Result<()> { - let contents = " -r - alacritty - -w - kitty - -t - /bin/firefox - "; - - let hotkey_1 = Hotkey::new(evdev::Key::KEY_R, vec![], String::from("alacritty")); - let hotkey_2 = Hotkey::new(evdev::Key::KEY_W, vec![], String::from("kitty")); - let hotkey_3 = Hotkey::new(evdev::Key::KEY_T, vec![], String::from("/bin/firefox")); - - eval_config_test(contents, vec![hotkey_1, hotkey_2, hotkey_3]) - } - - #[test] - fn test_comments() -> std::io::Result<()> { - let contents = " -r - alacritty - -w - kitty - -#t - #/bin/firefox - "; - - let expected_keybinds = vec![ - Hotkey::new(evdev::Key::KEY_R, vec![], String::from("alacritty")), - Hotkey::new(evdev::Key::KEY_W, vec![], String::from("kitty")), - ]; - - eval_config_test(contents, expected_keybinds) - } - - #[test] - fn test_multiple_keypress() -> std::io::Result<()> { - let contents = " -super + 5 - alacritty - "; - - let expected_keybinds = - vec![Hotkey::new(evdev::Key::KEY_5, vec![Modifier::Super], String::from("alacritty"))]; - - eval_config_test(contents, expected_keybinds) - } - - #[test] - fn test_keysym_instead_of_modifier() -> std::io::Result<()> { - let contents = " -shift + k + m - notify-send 'Hello world!' - "; - - eval_invalid_config_test(contents, ParseError::InvalidModifier(PathBuf::new(), 2)) - } - - #[test] - fn test_modifier_instead_of_keysym() -> std::io::Result<()> { - let contents = " -shift + k + alt - notify-send 'Hello world!' - "; - - eval_invalid_config_test(contents, ParseError::InvalidModifier(PathBuf::new(), 2)) - } - - #[test] - fn test_unfinished_plus_sign() -> std::io::Result<()> { - let contents = " - - -shift + alt + - notify-send 'Hello world!' - "; - - eval_invalid_config_test(contents, ParseError::UnknownSymbol(PathBuf::new(), 4)) - } - - #[test] - fn test_plus_sign_at_start() -> std::io::Result<()> { - let contents = " -+ shift + k - notify-send 'Hello world!' - "; - - eval_invalid_config_test(contents, ParseError::UnknownSymbol(PathBuf::new(), 2)) - } - - #[test] - fn test_common_modifiers() -> std::io::Result<()> { - let contents = " -shift + k - notify-send 'Hello world!' - -control + 5 - notify-send 'Hello world!' - -alt + 2 - notify-send 'Hello world!' - -altgr + i - notify-send 'Hello world!' - -super + z - notify-send 'Hello world!' - "; - - let expected_hotkeys = vec![ - Hotkey::new( - evdev::Key::KEY_K, - vec![Modifier::Shift], - "notify-send 'Hello world!'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_5, - vec![Modifier::Control], - "notify-send 'Hello world!'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_2, - vec![Modifier::Alt], - "notify-send 'Hello world!'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_I, - vec![Modifier::Altgr], - "notify-send 'Hello world!'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_Z, - vec![Modifier::Super], - "notify-send 'Hello world!'".to_string(), - ), - ]; - - eval_config_test(contents, expected_hotkeys) - } - - #[test] - fn test_command_with_many_spaces() -> std::io::Result<()> { - let contents = " -p - xbacklight -inc 10 -fps 30 -time 200 - "; - - let expected_keybinds = vec![Hotkey::new( - evdev::Key::KEY_P, - vec![], - String::from("xbacklight -inc 10 -fps 30 -time 200"), - )]; - - eval_config_test(contents, expected_keybinds) - } - - #[test] - fn test_invalid_keybinding() -> std::io::Result<()> { - let contents = " -p - xbacklight -inc 10 -fps 30 -time 200 - -pesto - xterm - "; - - eval_invalid_config_test(contents, ParseError::UnknownSymbol(PathBuf::new(), 5)) - } - - #[test] - // keysyms not followed by command should be ignored - fn test_no_command() -> std::io::Result<()> { - let contents = " -k - xbacklight -inc 10 -fps 30 -time 200 - -w - - "; - - eval_config_test( - contents, - vec![Hotkey::new( - evdev::Key::KEY_K, - vec![], - "xbacklight -inc 10 -fps 30 -time 200".to_string(), - )], - ) - } - - #[test] - fn test_real_config_snippet() -> std::io::Result<()> { - let contents = " -# reloads sxhkd configuration: -super + Escape - pkill -USR1 -x sxhkd ; sxhkd & - -# Launch Terminal -super + Return - alacritty -t \"Terminal\" -e \"$HOME/.config/sxhkd/new_tmux_terminal.sh\" - -# terminal emulator (no tmux) -super + shift + Return - alacritty -t \"Terminal\" - -# terminal emulator (new tmux session) -alt + Return - alacritty -t \"Terminal\" -e \"tmux\" - -ctrl + 0 - play-song.sh - -super + minus - play-song.sh album - "; - - let expected_result: Vec = vec![ - Hotkey::new( - evdev::Key::KEY_ESC, - vec![Modifier::Super], - String::from("pkill -USR1 -x sxhkd ; sxhkd &"), - ), - Hotkey::new( - evdev::Key::KEY_ENTER, - vec![Modifier::Super], - String::from( - "alacritty -t \"Terminal\" -e \"$HOME/.config/sxhkd/new_tmux_terminal.sh\"", - ), - ), - Hotkey::new( - evdev::Key::KEY_ENTER, - vec![Modifier::Super, Modifier::Shift], - String::from("alacritty -t \"Terminal\""), - ), - Hotkey::new( - evdev::Key::KEY_ENTER, - vec![Modifier::Alt], - String::from("alacritty -t \"Terminal\" -e \"tmux\""), - ), - Hotkey::new(evdev::Key::KEY_0, vec![Modifier::Control], String::from("play-song.sh")), - Hotkey::new( - evdev::Key::KEY_MINUS, - vec![Modifier::Super], - String::from("play-song.sh album"), - ), - ]; - - eval_config_test(contents, expected_result) - } - - #[test] - fn test_multiline_command() -> std::io::Result<()> { - let contents = " -k - mpc ls | dmenu | \\ - sed -i 's/foo/bar/g' - "; - - let expected_keybind = Hotkey::new( - evdev::Key::KEY_K, - vec![], - String::from("mpc ls | dmenu | sed -i 's/foo/bar/g'"), - ); - - eval_config_test(contents, vec![expected_keybind]) - } - - #[test] - fn test_commented_out_keybind() -> std::io::Result<()> { - let contents = " -#w - gimp - "; - - eval_config_test(contents, vec![]) - } - - // TODO: Write these tests as needed. - - #[test] - fn test_all_alphanumeric() -> std::io::Result<()> { - let symbols: [&str; 36] = [ - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", - "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", - "8", "9", - ]; - let keysyms: [evdev::Key; 36] = [ - evdev::Key::KEY_A, - evdev::Key::KEY_B, - evdev::Key::KEY_C, - evdev::Key::KEY_D, - evdev::Key::KEY_E, - evdev::Key::KEY_F, - evdev::Key::KEY_G, - evdev::Key::KEY_H, - evdev::Key::KEY_I, - evdev::Key::KEY_J, - evdev::Key::KEY_K, - evdev::Key::KEY_L, - evdev::Key::KEY_M, - evdev::Key::KEY_N, - evdev::Key::KEY_O, - evdev::Key::KEY_P, - evdev::Key::KEY_Q, - evdev::Key::KEY_R, - evdev::Key::KEY_S, - evdev::Key::KEY_T, - evdev::Key::KEY_U, - evdev::Key::KEY_V, - evdev::Key::KEY_W, - evdev::Key::KEY_X, - evdev::Key::KEY_Y, - evdev::Key::KEY_Z, - evdev::Key::KEY_0, - evdev::Key::KEY_1, - evdev::Key::KEY_2, - evdev::Key::KEY_3, - evdev::Key::KEY_4, - evdev::Key::KEY_5, - evdev::Key::KEY_6, - evdev::Key::KEY_7, - evdev::Key::KEY_8, - evdev::Key::KEY_9, - ]; - - let mut contents = String::new(); - for symbol in &symbols { - contents.push_str(&format!("{}\n st\n", symbol)); - } - let contents = &contents; - - let expected_result: Vec = - keysyms.iter().map(|keysym| Hotkey::new(*keysym, vec![], "st".to_string())).collect(); - - eval_config_test(contents, expected_result) - } - - #[test] - fn test_homerow_special_keys_top() -> std::io::Result<()> { - let symbols: [&str; 7] = - ["Escape", "BackSpace", "Return", "Tab", "minus", "equal", "grave"]; - - let keysyms: [evdev::Key; 7] = [ - evdev::Key::KEY_ESC, - evdev::Key::KEY_BACKSPACE, - evdev::Key::KEY_ENTER, - evdev::Key::KEY_TAB, - evdev::Key::KEY_MINUS, - evdev::Key::KEY_EQUAL, - evdev::Key::KEY_GRAVE, - ]; - - let mut contents = String::new(); - for symbol in &symbols { - contents.push_str(&format!("{}\n st\n", symbol)); - } - let contents = &contents; - - let expected_result: Vec = - keysyms.iter().map(|keysym| Hotkey::new(*keysym, vec![], "st".to_string())).collect(); - - eval_config_test(contents, expected_result) - } - - #[test] - fn test_case_insensitive() -> std::io::Result<()> { - let contents = " -Super + SHIFT + alt + a - st -ReTurn - ts - "; - eval_config_test( - contents, - vec![ - Hotkey::new( - evdev::Key::KEY_A, - vec![Modifier::Super, Modifier::Shift, Modifier::Alt], - "st".to_string(), - ), - Hotkey::new(evdev::Key::KEY_ENTER, vec![], "ts".to_string()), - ], - ) - } - - #[test] - fn test_duplicate_hotkeys() -> std::io::Result<()> { - let contents = " -super + shift + a - st -shift + suPer + A - ts -b - st -B - ts -"; - eval_config_test( - contents, - vec![ - Hotkey::new( - evdev::Key::KEY_A, - vec![Modifier::Super, Modifier::Shift], - "ts".to_string(), - ), - Hotkey::new(evdev::Key::KEY_B, vec![], "ts".to_string()), - ], - ) - } - - #[test] - fn test_inline_comment() -> std::io::Result<()> { - let contents = " -super + a #comment and comment super - st -super + shift + b - ts #this comment should be handled by shell -" - .to_string(); - eval_config_test( - &contents, - vec![ - Hotkey::new(evdev::Key::KEY_A, vec![Modifier::Super], "st".to_string()), - Hotkey::new( - evdev::Key::KEY_B, - vec![Modifier::Super, Modifier::Shift], - "ts #this comment should be handled by shell".to_string(), - ), - ], - ) - } - - #[test] - fn test_blank_config() -> std::io::Result<()> { - let contents = ""; - - eval_config_test(contents, vec![]) - } - - #[test] - fn test_blank_config_with_whitespace() -> std::io::Result<()> { - let contents = " - - - "; - - eval_config_test(contents, vec![]) - } - - #[test] - fn test_extract_curly_brace() -> std::io::Result<()> { - let keybind_with_curly_brace = "super + {a,b,c}"; - assert_eq!( - extract_curly_brace(keybind_with_curly_brace), - vec!["super + a", "super + b", "super + c",] - ); - let command_with_curly_brace = "bspc node -p {west,south,north,west}"; - assert_eq!( - extract_curly_brace(command_with_curly_brace), - vec![ - "bspc node -p west", - "bspc node -p south", - "bspc node -p north", - "bspc node -p west", - ] - ); - let wrong_format = "super + }a, b, c{"; - assert_eq!(extract_curly_brace(wrong_format), vec![wrong_format]); - let single_sym = "super + {a}"; - assert_eq!(extract_curly_brace(single_sym), vec!["super + a"]); - Ok(()) - } - - #[test] - fn test_curly_brace() -> std::io::Result<()> { - let contents = " -super + {a,b,c} - {firefox, brave, chrome}"; - eval_config_test( - contents, - vec![ - Hotkey::new(evdev::Key::KEY_A, vec![Modifier::Super], "firefox".to_string()), - Hotkey::new(evdev::Key::KEY_B, vec![Modifier::Super], "brave".to_string()), - Hotkey::new(evdev::Key::KEY_C, vec![Modifier::Super], "chrome".to_string()), - ], - ) - } - - #[test] - fn test_curly_brace_less_commands() -> std::io::Result<()> { - let contents = " -super + {a,b,c} - {firefox, brave}"; - eval_config_test( - contents, - vec![ - Hotkey::new(evdev::Key::KEY_A, vec![Modifier::Super], "firefox".to_string()), - Hotkey::new(evdev::Key::KEY_B, vec![Modifier::Super], "brave".to_string()), - ], - ) - } - - #[test] - fn test_curly_brace_less_keysyms() -> std::io::Result<()> { - let contents = " -super + {a, b} - {firefox, brave, chrome}"; - eval_config_test( - contents, - vec![ - Hotkey::new(evdev::Key::KEY_A, vec![Modifier::Super], "firefox".to_string()), - Hotkey::new(evdev::Key::KEY_B, vec![Modifier::Super], "brave".to_string()), - ], - ) - } - - #[test] - fn test_range_syntax() -> std::io::Result<()> { - let contents = " -super + {1-9,0} - bspc desktop -f '{1-9,0}'"; - eval_config_test( - contents, - vec![ - Hotkey::new( - evdev::Key::KEY_1, - vec![Modifier::Super], - "bspc desktop -f '1'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_2, - vec![Modifier::Super], - "bspc desktop -f '2'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_3, - vec![Modifier::Super], - "bspc desktop -f '3'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_4, - vec![Modifier::Super], - "bspc desktop -f '4'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_5, - vec![Modifier::Super], - "bspc desktop -f '5'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_6, - vec![Modifier::Super], - "bspc desktop -f '6'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_7, - vec![Modifier::Super], - "bspc desktop -f '7'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_8, - vec![Modifier::Super], - "bspc desktop -f '8'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_9, - vec![Modifier::Super], - "bspc desktop -f '9'".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_0, - vec![Modifier::Super], - "bspc desktop -f '0'".to_string(), - ), - ], - ) - } - - #[test] - fn test_range_syntax_ascii_character() -> std::io::Result<()> { - let contents = " -super + {a-c} - {firefox, brave, chrome}"; - eval_config_test( - contents, - vec![ - Hotkey::new(evdev::Key::KEY_A, vec![Modifier::Super], "firefox".to_string()), - Hotkey::new(evdev::Key::KEY_B, vec![Modifier::Super], "brave".to_string()), - Hotkey::new(evdev::Key::KEY_C, vec![Modifier::Super], "chrome".to_string()), - ], - ) - } - - #[test] - fn test_range_syntax_not_ascii() -> std::io::Result<()> { - let contents = " -super + {a-是} - {firefox, brave} - "; - eval_invalid_config_test(contents, ParseError::UnknownSymbol(PathBuf::new(), 2)) - } - - #[test] - fn test_range_syntax_invalid_range() -> std::io::Result<()> { - let contents = " -super + {bc-ad} - {firefox, brave} - "; - eval_invalid_config_test(contents, ParseError::UnknownSymbol(PathBuf::new(), 2)) - } - - #[test] - fn test_ranger_syntax_not_full_range() -> std::io::Result<()> { - let contents = " -super + {a-} - {firefox, brave}"; - eval_invalid_config_test(contents, ParseError::UnknownSymbol(PathBuf::new(), 2)) - } - - #[test] - fn test_none() -> std::io::Result<()> { - let contents = " -super + {_, shift} + b - {firefox, brave}"; - eval_config_test( - contents, - vec![ - Hotkey::new(evdev::Key::KEY_B, vec![Modifier::Super], "firefox".to_string()), - Hotkey::new( - evdev::Key::KEY_B, - vec![Modifier::Super, Modifier::Shift], - "brave".to_string(), - ), - ], - ) - } - - #[test] - fn test_multiple_ranges() -> std::io::Result<()> { - let contents = " -super + {shift,alt} + {c,d} - {librewolf, firefox} {--sync, --help} - "; - - eval_config_test( - contents, - vec![ - Hotkey::new( - evdev::Key::KEY_C, - vec![Modifier::Super, Modifier::Shift], - "librewolf --sync".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_D, - vec![Modifier::Super, Modifier::Shift], - "librewolf --help".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_C, - vec![Modifier::Super, Modifier::Alt], - "firefox --sync".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_D, - vec![Modifier::Super, Modifier::Alt], - "firefox --help".to_string(), - ), - ], - ) - } - - #[test] - fn test_multiple_ranges_numbers() -> std::io::Result<()> { - let contents = " -{control,super} + {1-3} - {notify-send, echo} {hello,how,are} - "; - - eval_config_test( - contents, - vec![ - Hotkey::new( - evdev::Key::KEY_1, - vec![Modifier::Control], - "notify-send hello".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_2, - vec![Modifier::Control], - "notify-send how".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_3, - vec![Modifier::Control], - "notify-send are".to_string(), - ), - Hotkey::new(evdev::Key::KEY_1, vec![Modifier::Super], "echo hello".to_string()), - Hotkey::new(evdev::Key::KEY_2, vec![Modifier::Super], "echo how".to_string()), - Hotkey::new(evdev::Key::KEY_3, vec![Modifier::Super], "echo are".to_string()), - ], - ) - } - - #[test] - fn test_bspwm_multiple_curly_brace() -> std::io::Result<()> { - let contents = " -super + {_,shift + }{h,j,k,l} - bspc node -{f,s} {west,south,north,east}"; - - eval_config_test( - contents, - vec![ - Hotkey::new( - evdev::Key::KEY_H, - vec![Modifier::Super], - "bspc node -f west".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_J, - vec![Modifier::Super], - "bspc node -f south".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_K, - vec![Modifier::Super], - "bspc node -f north".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_L, - vec![Modifier::Super], - "bspc node -f east".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_H, - vec![Modifier::Super, Modifier::Shift], - "bspc node -s west".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_J, - vec![Modifier::Super, Modifier::Shift], - "bspc node -s south".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_K, - vec![Modifier::Super, Modifier::Shift], - "bspc node -s north".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_L, - vec![Modifier::Super, Modifier::Shift], - "bspc node -s east".to_string(), - ), - ], - ) - } - - #[test] - fn test_longer_multiple_curly_brace() -> std::io::Result<()> { - let contents = " -super + {_, ctrl +} {_, shift +} {1-2} - riverctl {set, toggle}-{focused, view}-tags {1-2}"; - eval_config_test( - contents, - vec![ - Hotkey::new( - evdev::Key::KEY_1, - vec![Modifier::Super], - "riverctl set-focused-tags 1".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_2, - vec![Modifier::Super], - "riverctl set-focused-tags 2".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_1, - vec![Modifier::Super, Modifier::Control], - "riverctl toggle-focused-tags 1".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_2, - vec![Modifier::Super, Modifier::Control], - "riverctl toggle-focused-tags 2".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_1, - vec![Modifier::Super, Modifier::Shift], - "riverctl set-view-tags 1".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_2, - vec![Modifier::Super, Modifier::Shift], - "riverctl set-view-tags 2".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_1, - vec![Modifier::Super, Modifier::Control, Modifier::Shift], - "riverctl toggle-view-tags 1".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_2, - vec![Modifier::Super, Modifier::Control, Modifier::Shift], - "riverctl toggle-view-tags 2".to_string(), - ), - ], - ) - } - - #[test] - fn test_period_binding() -> std::io::Result<()> { - let contents = " -super + {comma, period} - riverctl focus-output {previous, next}"; - - eval_config_test( - contents, - vec![ - Hotkey::new( - evdev::Key::KEY_COMMA, - vec![Modifier::Super], - "riverctl focus-output previous".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_DOT, - vec![Modifier::Super], - "riverctl focus-output next".to_string(), - ), - ], - ) - } - - #[test] - fn test_period_escape_binding() -> std::io::Result<()> { - let contents = " -super + {\\,, .} - riverctl focus-output {previous, next}"; - - eval_config_test( - contents, - vec![ - Hotkey::new( - evdev::Key::KEY_COMMA, - vec![Modifier::Super], - "riverctl focus-output previous".to_string(), - ), - Hotkey::new( - evdev::Key::KEY_DOT, - vec![Modifier::Super], - "riverctl focus-output next".to_string(), - ), - ], - ) - } - - #[test] - fn test_prefix() -> std::io::Result<()> { - let contents = " -super + @1 - 1 -super + ~2 - 2 -super + ~@3 - 3 -super + @~4 - 4"; - - eval_config_test( - contents, - vec![ - Hotkey::new(evdev::Key::KEY_1, vec![Modifier::Super], "1".to_string()).on_release(), - Hotkey::new(evdev::Key::KEY_2, vec![Modifier::Super], "2".to_string()).send(), - Hotkey::new(evdev::Key::KEY_3, vec![Modifier::Super], "3".to_string()) - .on_release() - .send(), - Hotkey::new(evdev::Key::KEY_4, vec![Modifier::Super], "4".to_string()) - .on_release() - .send(), - ], - ) - } - - #[test] - fn test_override() -> std::io::Result<()> { - let contents = " -super + a - 1 -super + a - 2"; - eval_config_test( - contents, - vec![Hotkey::new(evdev::Key::KEY_A, vec![Modifier::Super], "2".to_string())], - ) - } - - #[test] - fn test_any_modifier() -> std::io::Result<()> { - let contents = " -any + a - 1"; - eval_config_test( - contents, - vec![Hotkey::new(evdev::Key::KEY_A, vec![Modifier::Any], "1".to_string())], - ) - } -} - -mod test_config_display { - use crate::config::{Error, ParseError}; - use std::io; - use std::path::PathBuf; - - #[test] - fn test_display_io_error() { - let error = Error::Io(io::Error::from(io::ErrorKind::UnexpectedEof)); - - if !format!("{}", error).contains("unexpected end of file") { - panic!("Error message was '{}", error); - } - } - - #[test] - fn test_display_unknown_symbol_error() { - let error = Error::InvalidConfig(ParseError::UnknownSymbol(PathBuf::new(), 10)); - - assert_eq!( - format!("{}", error), - "Error parsing config file \"\". Unknown symbol at line 10." - ); - } - - #[test] - fn test_display_invalid_modifier_error() { - let error = Error::InvalidConfig(ParseError::InvalidModifier(PathBuf::new(), 25)); - - assert_eq!( - format!("{}", error), - "Error parsing config file \"\". Invalid modifier at line 25." - ); - } - - #[test] - fn test_invalid_keysm_error() { - let error = Error::InvalidConfig(ParseError::InvalidKeysym(PathBuf::new(), 7)); - - assert_eq!( - format!("{}", error), - "Error parsing config file \"\". Invalid keysym at line 7." - ); - } -}