improve doc and restructure a bit

This commit is contained in:
Alexander Mohr 2025-05-01 00:59:30 +02:00
parent bb4dcb6908
commit 88c2cfbd8e
6 changed files with 236 additions and 649 deletions

445
Cargo.lock generated
View file

@ -2,34 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"serde",
"version_check",
"zerocopy",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.3" version = "1.1.3"
@ -75,7 +47,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@ -86,7 +58,7 @@ checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"once_cell", "once_cell",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@ -95,28 +67,6 @@ version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]]
name = "async-stream"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
dependencies = [
"async-stream-impl",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-stream-impl"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -134,21 +84,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -161,12 +96,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]] [[package]]
name = "cairo-rs" name = "cairo-rs"
version = "0.20.7" version = "0.20.7"
@ -333,27 +262,6 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "derive_more"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]] [[package]]
name = "dirs" name = "dirs"
version = "6.0.0" version = "6.0.0"
@ -372,7 +280,7 @@ dependencies = [
"libc", "libc",
"option-ext", "option-ext",
"redox_users", "redox_users",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@ -423,7 +331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@ -458,21 +366,6 @@ dependencies = [
"xdgkit", "xdgkit",
] ]
[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.31" version = "0.3.31"
@ -480,7 +373,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink",
] ]
[[package]] [[package]]
@ -506,16 +398,6 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-lite"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.31" version = "0.3.31"
@ -527,12 +409,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.31" version = "0.3.31"
@ -545,13 +421,9 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [ dependencies = [
"futures-channel",
"futures-core", "futures-core",
"futures-io",
"futures-macro", "futures-macro",
"futures-sink",
"futures-task", "futures-task",
"memchr",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab", "slab",
@ -626,12 +498,6 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]] [[package]]
name = "gio" name = "gio"
version = "0.20.9" version = "0.20.9"
@ -659,7 +525,7 @@ dependencies = [
"gobject-sys", "gobject-sys",
"libc", "libc",
"system-deps", "system-deps",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@ -898,39 +764,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hyprland"
version = "0.4.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc9c1413b6f0fd10b2e4463479490e30b2497ae4449f044da16053f5f2cb03b8"
dependencies = [
"ahash",
"async-stream",
"derive_more",
"either",
"futures-lite",
"hyprland-macros",
"num-traits",
"once_cell",
"paste",
"phf",
"serde",
"serde_json",
"serde_repr",
"tokio",
]
[[package]]
name = "hyprland-macros"
version = "0.4.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69e3cbed6e560408051175d29a9ed6ad1e64a7ff443836addf797b0479f58983"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.3" version = "1.9.3"
@ -1021,16 +854,6 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.27" version = "0.4.27"
@ -1068,26 +891,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "1.2.4" version = "1.2.4"
@ -1104,24 +907,6 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.3" version = "1.21.3"
@ -1164,35 +949,6 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "petgraph" name = "petgraph"
version = "0.6.5" version = "0.6.5"
@ -1203,48 +959,6 @@ dependencies = [
"indexmap 2.9.0", "indexmap 2.9.0",
] ]
[[package]]
name = "phf"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_macros",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher",
]
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.16" version = "0.2.16"
@ -1315,21 +1029,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.10.0" version = "1.10.0"
@ -1350,15 +1049,6 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "redox_syscall"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
dependencies = [
"bitflags 2.9.0",
]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.5.0" version = "0.5.0"
@ -1399,12 +1089,6 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.1" version = "0.4.1"
@ -1424,7 +1108,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@ -1433,12 +1117,6 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.26" version = "1.0.26"
@ -1477,17 +1155,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_repr"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.8" version = "0.6.8"
@ -1497,21 +1164,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]]
name = "siphasher"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -1527,16 +1179,6 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "socket2"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"
@ -1620,35 +1262,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e004df4c5f0805eb5f55883204a514cfa43a6d924741be29e871753a53d5565a" checksum = "e004df4c5f0805eb5f55883204a514cfa43a6d924741be29e871753a53d5565a"
[[package]]
name = "tokio"
version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.20" version = "0.8.20"
@ -1702,12 +1315,6 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@ -1720,12 +1327,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -1766,7 +1367,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys",
] ]
[[package]] [[package]]
@ -1775,15 +1376,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"
@ -1882,11 +1474,9 @@ dependencies = [
"dirs", "dirs",
"env_logger", "env_logger",
"freedesktop-file-parser", "freedesktop-file-parser",
"futures",
"gdk4", "gdk4",
"gtk4", "gtk4",
"gtk4-layer-shell", "gtk4-layer-shell",
"hyprland",
"libc", "libc",
"log", "log",
"meval", "meval",
@ -1896,7 +1486,6 @@ dependencies = [
"serde_json", "serde_json",
"strsim 0.11.1", "strsim 0.11.1",
"thiserror", "thiserror",
"tokio",
"toml", "toml",
"tree_magic_mini", "tree_magic_mini",
"which", "which",
@ -1928,23 +1517,3 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [ dependencies = [
"linked-hash-map", "linked-hash-map",
] ]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

@ -21,6 +21,9 @@ name = "worf"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.docs.rs]
no-deps = true
[dependencies] [dependencies]
gtk4 = { version = "0.9.5", default-features = true, features = ["v4_6"] } gtk4 = { version = "0.9.5", default-features = true, features = ["v4_6"] }
gtk4-layer-shell = "0.5.0" gtk4-layer-shell = "0.5.0"
@ -29,7 +32,6 @@ anyhow = "1.0.97"
env_logger = "0.11.8" env_logger = "0.11.8"
log = "0.4.27" log = "0.4.27"
regex = "1.11.1" regex = "1.11.1"
hyprland = "0.4.0-beta.2"
clap = { version = "4.5.35", features = ["derive"] } clap = { version = "4.5.35", features = ["derive"] }
thiserror = "2.0.12" thiserror = "2.0.12"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
@ -44,5 +46,3 @@ which = "7.0.3"
meval = "0.2.0" meval = "0.2.0"
tree_magic_mini = "3.1.6" tree_magic_mini = "3.1.6"
rayon = "1.10.0" rayon = "1.10.0"
tokio = { version = "1.44.2", features = ["full"] }
futures = "0.3.31"

View file

@ -1,63 +1,17 @@
use std::collections::HashMap; use crate::Error;
use std::path::Path; use crate::config::expand_path;
use std::path::PathBuf;
use std::time::Instant;
use std::{env, fs};
use freedesktop_file_parser::DesktopFile; use freedesktop_file_parser::DesktopFile;
use rayon::prelude::*; use rayon::prelude::*;
use regex::Regex; use regex::Regex;
use std::collections::HashMap;
use std::os::unix::prelude::CommandExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::time::Instant;
use std::{env, fs, io};
#[derive(Debug)] /// Returns a regex with supported image extensions
pub enum DesktopError {
MissingIcon,
ParsingError(String),
}
//
// /// # Errors
// ///
// /// Will return `Err` if no icon can be found
// pub fn default_icon() -> Result<String, DesktopError> {
// fetch_icon_from_theme("image-missing").map_err(|_| DesktopError::MissingIcon)
// }
//
// fn fetch_icon_from_theme(icon_name: &str) -> Result<String, DesktopError> {
// let display = Display::default();
// if display.is_none() {
// log::error!("Failed to get display");
// }
//
// let display = Display::default().expect("Failed to get default display");
// let theme = IconTheme::for_display(&display);
//
// let icon = theme.lookup_icon(
// icon_name,
// &[],
// 32,
// 1,
// TextDirection::None,
// IconLookupFlags::empty(),
// );
//
// match icon
// .file()
// .and_then(|file| file.path())
// .and_then(|path| path.to_str().map(string::ToString::to_string))
// {
// None => {
// let path = PathBuf::from("/usr/share/icons")
// .join(theme.theme_name())
// .join(format!("{icon_name}.svg"));
// if path.exists() {
// Ok(path.display().to_string())
// } else {
// Err(DesktopError::MissingIcon)
// }
// }
// Some(i) => Ok(i),
// }
// }use futures::StreamExt;
/// # Panics /// # Panics
/// ///
/// When it cannot parse the internal regex /// When it cannot parse the internal regex
@ -67,11 +21,16 @@ pub fn known_image_extension_regex_pattern() -> Regex {
.expect("Internal image regex is not valid anymore.") .expect("Internal image regex is not valid anymore.")
} }
/// Read an icon from a shared directory
/// * /usr/local/share/icon
/// * /usr/share/icons
/// * /usr/share/pixmaps
/// * $HOME/.local/share/icon (if exists)
/// # Errors /// # Errors
/// ///
/// Will return `Err` /// Will return `Err`
/// * if it was not able to find any icon /// * if it was not able to find any icon
pub fn fetch_icon_from_common_dirs(icon_name: &str) -> Result<String, DesktopError> { pub fn fetch_icon_from_common_dirs(icon_name: &str) -> Result<String, Error> {
let mut paths = vec![ let mut paths = vec![
PathBuf::from("/usr/local/share/icons"), PathBuf::from("/usr/local/share/icons"),
PathBuf::from("/usr/share/icons"), PathBuf::from("/usr/share/icons"),
@ -88,19 +47,20 @@ pub fn fetch_icon_from_common_dirs(icon_name: &str) -> Result<String, DesktopErr
.into_iter() .into_iter()
.filter(|dir| dir.exists()) .filter(|dir| dir.exists())
.find_map(|dir| { .find_map(|dir| {
find_file_case_insensitive(dir.as_path(), &formatted_name) find_file_via_regex(dir.as_path(), &formatted_name)
.and_then(|files| files.first().map(|f| f.to_string_lossy().into_owned())) .and_then(|files| files.first().map(|f| f.to_string_lossy().into_owned()))
}) })
.ok_or(DesktopError::MissingIcon) .ok_or(Error::MissingIcon)
} else { } else {
Err(DesktopError::ParsingError( Err(Error::ParsingError(
"Failed to get formatted icon, likely the internal regex did not parse properly" "Failed to get formatted icon, likely the internal regex did not parse properly"
.to_string(), .to_string(),
)) ))
} }
} }
fn find_file_case_insensitive(folder: &Path, file_name: &Regex) -> Option<Vec<PathBuf>> { /// Helper function to retrieve a file with given regex.
fn find_file_via_regex(folder: &Path, file_name: &Regex) -> Option<Vec<PathBuf>> {
if !folder.exists() || !folder.is_dir() { if !folder.exists() || !folder.is_dir() {
return None; return None;
} }
@ -119,6 +79,10 @@ fn find_file_case_insensitive(folder: &Path, file_name: &Regex) -> Option<Vec<Pa
}) })
} }
/// Parse all desktop files in known locations
/// * /usr/share/applications
/// * /usr/local/share/applications
/// * /var/lib/flatpak/exports/share/applications
/// # Panics /// # Panics
/// ///
/// When it cannot parse the internal regex /// When it cannot parse the internal regex
@ -151,7 +115,7 @@ pub fn find_desktop_files() -> Vec<DesktopFile> {
let p: Vec<_> = paths let p: Vec<_> = paths
.into_par_iter() .into_par_iter()
.filter(|desktop_dir| desktop_dir.exists()) .filter(|desktop_dir| desktop_dir.exists())
.filter_map(|icon_dir| find_file_case_insensitive(&icon_dir, regex)) .filter_map(|icon_dir| find_file_via_regex(&icon_dir, regex))
.flat_map(|desktop_files| { .flat_map(|desktop_files| {
desktop_files.into_par_iter().filter_map(|desktop_file| { desktop_files.into_par_iter().filter_map(|desktop_file| {
fs::read_to_string(desktop_file) fs::read_to_string(desktop_file)
@ -164,30 +128,7 @@ pub fn find_desktop_files() -> Vec<DesktopFile> {
p p
} }
/// # Panics /// Return all possible locales based on the users preferences
///
/// When it cannot parse the internal regex
#[must_use]
pub fn lookup_icon(name: &str, size: i32) -> gtk4::Image {
let img_regex = Regex::new(&format!(
r"((?i).*{})|(^/.*)",
known_image_extension_regex_pattern()
));
let image = if img_regex.expect("invalid icon regex").is_match(name) {
if let Ok(img) = fetch_icon_from_common_dirs(name) {
gtk4::Image::from_file(img)
} else {
gtk4::Image::from_icon_name(name)
}
} else {
gtk4::Image::from_icon_name(name)
};
image.set_pixel_size(size);
image
}
#[must_use] #[must_use]
pub fn get_locale_variants() -> Vec<String> { pub fn get_locale_variants() -> Vec<String> {
let locale = env::var("LC_ALL") let locale = env::var("LC_ALL")
@ -208,6 +149,7 @@ pub fn get_locale_variants() -> Vec<String> {
variants variants
} }
/// Lookup a value from a hashmap with respect to current locale
// implicit hasher does not make sense here, it is only for desktop files // implicit hasher does not make sense here, it is only for desktop files
#[allow(clippy::implicit_hasher)] #[allow(clippy::implicit_hasher)]
#[must_use] #[must_use]
@ -222,3 +164,107 @@ pub fn lookup_name_with_locale(
.map(std::borrow::ToOwned::to_owned) .map(std::borrow::ToOwned::to_owned)
.or_else(|| Some(fallback.to_owned())) .or_else(|| Some(fallback.to_owned()))
} }
/// Spawn a new process and forks it away from the current worf process
/// # Errors
/// * No action in menu item
/// * Cannot run command (i.e. not found)
pub fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> Result<(), Error> {
// todo fix actions ??
// todo graphical disk map icon not working
let parts = cmd.split(' ').collect::<Vec<_>>();
if parts.is_empty() {
return Err(Error::MissingAction);
}
if let Some(dir) = working_dir {
env::set_current_dir(dir)
.map_err(|e| Error::RunFailed(format!("cannot set workdir {e}")))?;
}
let exec = parts[0].replace('"', "");
let args: Vec<_> = parts
.iter()
.skip(1)
.filter(|arg| !arg.starts_with('%'))
.map(|arg| expand_path(arg))
.collect();
unsafe {
let _ = Command::new(exec)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.pre_exec(|| {
libc::setsid();
Ok(())
})
.spawn();
}
Ok(())
}
/// Parse a simple toml cache file from the format below
/// "Key"=score
/// i.e.
/// "Firefox"=42
/// "Chrome"=12
/// "Files"=50
/// # Errors
/// Returns an Error when the given file is not found or did not parse.
pub fn load_cache_file(cache_path: Option<&PathBuf>) -> Result<HashMap<String, i64>, Error> {
let Some(path) = cache_path else {
return Err(Error::MissingFile);
};
let toml_content =
fs::read_to_string(path).map_err(|e| Error::UpdateCacheError(format!("{e}")))?;
let parsed: toml::Value = toml_content
.parse()
.map_err(|_| Error::ParsingError("failed to parse cache".to_owned()))?;
let mut result: HashMap<String, i64> = HashMap::new();
if let toml::Value::Table(table) = parsed {
for (key, val) in table {
if let toml::Value::Integer(i) = val {
result.insert(key, i);
} else {
log::warn!("Skipping key '{key}' because it's not an integer");
}
}
}
Ok(result)
}
/// Stores a cache file in the cache format. See `load_cache_file` for details.
/// # Errors
/// `Error::Parsing` if converting into toml was not possible
/// `Error::Io` if storing the file failed.
// implicit hasher does not make sense here, it is only for desktop files
#[allow(clippy::implicit_hasher)]
pub fn save_cache_file(path: &PathBuf, data: &HashMap<String, i64>) -> Result<(), Error> {
// Convert the HashMap to TOML string
let toml_string =
toml::ser::to_string(&data).map_err(|e| Error::ParsingError(e.to_string()))?;
fs::write(path, toml_string).map_err(|e| Error::Io(e.to_string()))?;
Ok(())
}
/// Crates a new file if it does not exist yet.
/// # Errors
/// `Errors::Io` if creating the file failed
pub fn create_file_if_not_exists(path: &PathBuf) -> Result<(), Error> {
let file = fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(path);
match file {
Ok(_) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
Err(e) => Err(Error::Io(e.to_string())),
}
}

View file

@ -78,19 +78,29 @@ impl From<config::Align> for Align {
} }
} }
/// An entry in the list of selectable items in the UI.
/// Supports nested items but these cannot nested again (only nesting with depth == 1 is supported)
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct MenuItem<T: Clone> { pub struct MenuItem<T: Clone> {
pub label: String, // todo support empty label? /// text to show in the UI
pub label: String,
/// optional icon, will use fallback icon if None is given
pub icon_path: Option<String>, pub icon_path: Option<String>,
/// the action to run when this is selected.
pub action: Option<String>, pub action: Option<String>,
/// Sub elements of this entry. If this already has a parent entry, nesting is not supported
pub sub_elements: Vec<MenuItem<T>>, pub sub_elements: Vec<MenuItem<T>>,
/// Working directory to run the action in.
pub working_dir: Option<String>, pub working_dir: Option<String>,
/// Initial sort score to display favourites at the top
pub initial_sort_score: f64, pub initial_sort_score: f64,
/// Allows to store arbitrary additional information /// Allows to store arbitrary additional information
pub data: Option<T>, pub data: Option<T>,
/// Score the item got in the current search
search_sort_score: f64, search_sort_score: f64,
/// True if the item is visible
visible: bool, visible: bool,
} }
@ -140,6 +150,7 @@ struct UiElements<T: Clone> {
menu_rows: ArcMenuMap<T>, menu_rows: ArcMenuMap<T>,
} }
/// Shows the user interface and **blocks** until the user selected an entry
/// # Errors /// # Errors
/// ///
/// Will return Err when the channel between the UI and this is broken /// Will return Err when the channel between the UI and this is broken
@ -953,6 +964,7 @@ fn percent_or_absolute(value: Option<&String>, base_value: i32) -> Option<i32> {
} }
} }
/// Sorts menu items in alphabetical order, while maintaining the initial score
// highly unlikely that we are dealing with > i64 items // highly unlikely that we are dealing with > i64 items
#[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_possible_wrap)]
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]

View file

@ -1,4 +1,60 @@
use std::fmt;
/// Defines error the lib can encounter
#[derive(Debug)]
pub enum Error {
/// Failed to update a cache file with the given reason.
UpdateCacheError(String),
/// A given or configured file was not found, will also be used when
/// cache files are missing.
MissingFile,
/// Failed to read form standard input. I.e. used for dmenu.
StdInReadFail,
/// The selection was invalid or looking up the element failed or another reason.
InvalidSelection,
/// The given parameters did not yield an icon.
MissingIcon,
/// Parsing a configuration or cache file failed.
ParsingError(String),
/// A menu item was expected to have an action but none was found.
MissingAction,
/// Running the action failed with the given reason.
RunFailed(String),
/// An IO operation failed
Io(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::UpdateCacheError(s) => write!(f, "UpdateCacheError {s}"),
Error::MissingAction => write!(f, "MissingAction"),
Error::StdInReadFail => write!(f, "StdInReadFail"),
Error::InvalidSelection => write!(f, "InvalidSelection"),
Error::MissingFile => {
write!(f, "MissingFile")
}
Error::MissingIcon => {
write!(f, "MissingIcon")
}
Error::ParsingError(s) => {
write!(f, "ParsingError {s}")
}
Error::RunFailed(s) => {
write!(f, "RunFailed {s}")
}
Error::Io(s) => {
write!(f, "IO {s}")
}
}
}
}
/// Configuration and command line parsing
pub mod config; pub mod config;
/// Desktop action like parsing desktop files and launching programs
pub mod desktop; pub mod desktop;
/// All things related to the user interface
pub mod gui; pub mod gui;
/// Out of the box supported modes, like drun, dmenu, etc...
pub mod mode; pub mod mode;

View file

@ -1,42 +1,19 @@
use crate::config::{Config, expand_path}; use crate::config::{Config, expand_path};
use crate::desktop::{find_desktop_files, get_locale_variants, lookup_name_with_locale}; use crate::desktop::{
use crate::gui; create_file_if_not_exists, find_desktop_files, get_locale_variants, load_cache_file,
lookup_name_with_locale, save_cache_file, spawn_fork,
};
use crate::gui::{ItemProvider, MenuItem}; use crate::gui::{ItemProvider, MenuItem};
use anyhow::Context; use crate::{Error, gui};
use freedesktop_file_parser::EntryType; use freedesktop_file_parser::EntryType;
use rayon::prelude::*; use rayon::prelude::*;
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::io::Read; use std::io::Read;
use std::os::unix::prelude::CommandExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::time::Instant; use std::time::Instant;
use std::{env, fmt, fs, io}; use std::{fs, io};
#[derive(Debug)]
pub enum ModeError {
UpdateCacheError(String),
MissingAction,
RunError(String),
MissingCache,
StdInReadFail,
InvalidSelection,
}
impl fmt::Display for ModeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ModeError::UpdateCacheError(s) => write!(f, "UpdateCacheError {s}"),
ModeError::MissingAction => write!(f, "MissingAction"),
ModeError::RunError(s) => write!(f, "RunError, {s}"),
ModeError::MissingCache => write!(f, "MissingCache"),
ModeError::StdInReadFail => write!(f, "StdInReadFail"),
&ModeError::InvalidSelection => write!(f, "InvalidSelection"),
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
struct DRunCache { struct DRunCache {
@ -434,11 +411,11 @@ struct DMenuProvider {
} }
impl DMenuProvider { impl DMenuProvider {
fn new() -> Result<DMenuProvider, ModeError> { fn new() -> Result<DMenuProvider, Error> {
let mut input = String::new(); let mut input = String::new();
io::stdin() io::stdin()
.read_to_string(&mut input) .read_to_string(&mut input)
.map_err(|_| ModeError::StdInReadFail)?; .map_err(|_| Error::StdInReadFail)?;
let items: Vec<MenuItem<String>> = input let items: Vec<MenuItem<String>> = input
.lines() .lines()
@ -523,10 +500,11 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
} }
} }
/// Shows the drun mode
/// # Errors /// # Errors
/// ///
/// Will return `Err` if it was not able to spawn the process /// Will return `Err` if it was not able to spawn the process
pub fn d_run(config: &Config) -> Result<(), ModeError> { pub fn d_run(config: &Config) -> Result<(), Error> {
let provider = DRunProvider::new(String::new()); let provider = DRunProvider::new(String::new());
let cache_path = provider.cache_path.clone(); let cache_path = provider.cache_path.clone();
let mut cache = provider.cache.clone(); let mut cache = provider.cache.clone();
@ -543,11 +521,12 @@ pub fn d_run(config: &Config) -> Result<(), ModeError> {
Ok(()) Ok(())
} }
/// Shows the auto mode
/// # Errors /// # Errors
/// ///
/// Will return `Err` /// Will return `Err`
/// * if it was not able to spawn the process /// * if it was not able to spawn the process
pub fn auto(config: &Config) -> Result<(), ModeError> { pub fn auto(config: &Config) -> Result<(), Error> {
let mut provider = AutoItemProvider::new(config); let mut provider = AutoItemProvider::new(config);
let cache_path = provider.drun.cache_path.clone(); let cache_path = provider.drun.cache_path.clone();
let mut cache = provider.drun.cache.clone(); let mut cache = provider.drun.cache.clone();
@ -592,11 +571,12 @@ pub fn auto(config: &Config) -> Result<(), ModeError> {
Ok(()) Ok(())
} }
/// Shows the file browser mode
/// # Errors /// # Errors
/// ///
/// Will return `Err` /// Will return `Err`
/// * if it was not able to spawn the process /// * if it was not able to spawn the process
pub fn file(config: &Config) -> Result<(), ModeError> { pub fn file(config: &Config) -> Result<(), Error> {
let provider = FileItemProvider::new(String::new()); let provider = FileItemProvider::new(String::new());
// todo ues a arc instead of cloning the config // todo ues a arc instead of cloning the config
@ -615,7 +595,7 @@ pub fn file(config: &Config) -> Result<(), ModeError> {
Ok(()) Ok(())
} }
fn ssh_launch<T: Clone>(menu_item: &MenuItem<T>, config: &Config) -> Result<(), ModeError> { fn ssh_launch<T: Clone>(menu_item: &MenuItem<T>, config: &Config) -> Result<(), Error> {
if let Some(action) = &menu_item.action { if let Some(action) = &menu_item.action {
spawn_fork(action, None)?; spawn_fork(action, None)?;
} else { } else {
@ -627,15 +607,16 @@ fn ssh_launch<T: Clone>(menu_item: &MenuItem<T>, config: &Config) -> Result<(),
spawn_fork(&cmd, None)?; spawn_fork(&cmd, None)?;
} }
} }
Err(ModeError::MissingAction) Err(Error::MissingAction)
} }
/// Shows the ssh mode
/// # Errors /// # Errors
/// ///
/// Will return `Err` /// Will return `Err`
/// * if it was not able to spawn the process /// * if it was not able to spawn the process
/// * if it didn't find a terminal /// * if it didn't find a terminal
pub fn ssh(config: &Config) -> Result<(), ModeError> { pub fn ssh(config: &Config) -> Result<(), Error> {
let provider = SshProvider::new(String::new(), config); let provider = SshProvider::new(String::new(), config);
let selection_result = gui::show(config.clone(), provider, true); let selection_result = gui::show(config.clone(), provider, true);
if let Ok(mi) = selection_result { if let Ok(mi) = selection_result {
@ -646,6 +627,7 @@ pub fn ssh(config: &Config) -> Result<(), ModeError> {
Ok(()) Ok(())
} }
/// Shows the math mode
pub fn math(config: &Config) { pub fn math(config: &Config) {
let mut cfg_clone = config.clone(); let mut cfg_clone = config.clone();
let mut calc: Vec<MenuItem<String>> = vec![]; let mut calc: Vec<MenuItem<String>> = vec![];
@ -663,10 +645,11 @@ pub fn math(config: &Config) {
} }
} }
/// Shows the dmenu mode
/// # Errors /// # Errors
/// ///
/// todo /// Forwards errors from the gui. See `gui::show` for details.
pub fn dmenu(config: &Config) -> Result<(), ModeError> { pub fn dmenu(config: &Config) -> Result<(), Error> {
let provider = DMenuProvider::new()?; let provider = DMenuProvider::new()?;
let selection_result = gui::show(config.clone(), provider, true); let selection_result = gui::show(config.clone(), provider, true);
@ -675,7 +658,7 @@ pub fn dmenu(config: &Config) -> Result<(), ModeError> {
println!("{}", s.label); println!("{}", s.label);
Ok(()) Ok(())
} }
Err(_) => Err(ModeError::InvalidSelection), Err(_) => Err(Error::InvalidSelection),
} }
} }
@ -683,7 +666,7 @@ fn update_drun_cache_and_run<T: Clone>(
cache_path: Option<PathBuf>, cache_path: Option<PathBuf>,
cache: &mut HashMap<String, i64>, cache: &mut HashMap<String, i64>,
selection_result: MenuItem<T>, selection_result: MenuItem<T>,
) -> Result<(), ModeError> { ) -> Result<(), Error> {
if let Some(cache_path) = cache_path { if let Some(cache_path) = cache_path {
*cache.entry(selection_result.label).or_insert(0) += 1; *cache.entry(selection_result.label).or_insert(0) += 1;
if let Err(e) = save_cache_file(&cache_path, cache) { if let Err(e) = save_cache_file(&cache_path, cache) {
@ -694,7 +677,7 @@ fn update_drun_cache_and_run<T: Clone>(
if let Some(action) = selection_result.action { if let Some(action) = selection_result.action {
spawn_fork(&action, selection_result.working_dir.as_ref()) spawn_fork(&action, selection_result.working_dir.as_ref())
} else { } else {
Err(ModeError::MissingAction) Err(Error::MissingAction)
} }
} }
@ -711,82 +694,3 @@ fn load_d_run_cache() -> (Option<PathBuf>, HashMap<String, i64>) {
}; };
(cache_path, d_run_cache) (cache_path, d_run_cache)
} }
fn save_cache_file(path: &PathBuf, data: &HashMap<String, i64>) -> anyhow::Result<()> {
// Convert the HashMap to TOML string
let toml_string = toml::ser::to_string(&data).map_err(|e| anyhow::anyhow!(e))?;
fs::write(path, toml_string).map_err(|e| anyhow::anyhow!(e))
}
fn load_cache_file(cache_path: Option<&PathBuf>) -> Result<HashMap<String, i64>, ModeError> {
let Some(path) = cache_path else {
return Err(ModeError::MissingCache);
};
let toml_content =
fs::read_to_string(path).map_err(|e| ModeError::UpdateCacheError(format!("{e}")))?;
let parsed: toml::Value = toml_content.parse().expect("Failed to parse TOML");
let mut result: HashMap<String, i64> = HashMap::new();
if let toml::Value::Table(table) = parsed {
for (key, val) in table {
if let toml::Value::Integer(i) = val {
result.insert(key, i);
} else {
log::warn!("Skipping key '{key}' because it's not an integer");
}
}
}
Ok(result)
}
fn create_file_if_not_exists(path: &PathBuf) -> anyhow::Result<()> {
let file = fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(path);
match file {
Ok(_) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
Err(e) => Err(e).context(format!("Failed to create file {}", path.display()))?,
}
}
fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> Result<(), ModeError> {
// todo fix actions ??
// todo graphical disk map icon not working
let parts = cmd.split(' ').collect::<Vec<_>>();
if parts.is_empty() {
return Err(ModeError::MissingAction);
}
if let Some(dir) = working_dir {
env::set_current_dir(dir)
.map_err(|e| ModeError::RunError(format!("cannot set workdir {e}")))?;
}
let exec = parts[0].replace('"', "");
let args: Vec<_> = parts
.iter()
.skip(1)
.filter(|arg| !arg.starts_with('%'))
.map(|arg| expand_path(arg))
.collect();
unsafe {
let _ = Command::new(exec)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.pre_exec(|| {
libc::setsid();
Ok(())
})
.spawn();
}
Ok(())
}