initial commit
This commit is contained in:
Executable
+1
@@ -0,0 +1 @@
|
||||
/target
|
||||
+625
@@ -0,0 +1,625 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "autosplit"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"humantime",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
|
||||
[[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 = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[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 = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||
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 = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"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 = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.39.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"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.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "autosplit"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.81"
|
||||
clap = "4.5.13"
|
||||
env_logger = "0.11.5"
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
serde_json = "1.0.121"
|
||||
thiserror = "1.0.63"
|
||||
tokio = { version = "1.39.2", features = ["full"] }
|
||||
tokio-stream = "0.1.15"
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
mod swayipc;
|
||||
|
||||
use swayipc::{Node, Workspace};
|
||||
|
||||
use crate::swayipc::{collect_leaves, SwayEvent, SwayEventType, SwayIpc, WindowChangeType};
|
||||
|
||||
pub fn get_current_leaves_here<'a>(root: &'a Node, workspaces: &'a [Workspace]) -> Vec<&'a Node> {
|
||||
let focused_ws_id = workspaces
|
||||
.iter()
|
||||
.find_map(|ws| {
|
||||
if ws.focused {
|
||||
Some(ws.name.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let outputs = &root.nodes;
|
||||
let focused_workspace = outputs
|
||||
.iter()
|
||||
.find_map(|output| {
|
||||
output
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|workspace| workspace.name.clone().unwrap_or_default() == focused_ws_id)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
collect_leaves(focused_workspace)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::init();
|
||||
let sway_ipc = SwayIpc::connect().await.unwrap();
|
||||
|
||||
// Subscribe to workspace and window events
|
||||
let mut workspace_event_rx = sway_ipc
|
||||
.subscribe([SwayEventType::Workspace, SwayEventType::Window])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
while let Some(event) = workspace_event_rx.recv().await {
|
||||
if let SwayEvent::Window(window_event) = event {
|
||||
if let WindowChangeType::New = window_event.change {
|
||||
let sway_ipc_clone = sway_ipc.clone();
|
||||
|
||||
// Handle the new layout setup
|
||||
let root = sway_ipc_clone.get_tree().await.unwrap();
|
||||
let workspaces = sway_ipc_clone.get_workspaces().await.unwrap();
|
||||
let leaves = get_current_leaves_here(&root, &workspaces);
|
||||
|
||||
if let (Some(first), Some(second)) = (leaves.first(), leaves.get(1)) {
|
||||
let mut command = format!("[con_id={}] splith", first.id);
|
||||
match leaves.get(2) {
|
||||
Some(third) => {
|
||||
command = format!(
|
||||
"{};[con_id={}] splitv;[con_id={}] mark target;[con_id={}] move container to mark target;[con_id={}] swap container with mark target;",
|
||||
command, second.id, third.id, second.id, second.id
|
||||
);
|
||||
}
|
||||
None => {
|
||||
command = format!("{};[con_id={}] splitv;", command, second.id);
|
||||
}
|
||||
}
|
||||
sway_ipc_clone.run_command(command).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+866
@@ -0,0 +1,866 @@
|
||||
use log::{debug, error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::fmt::Display;
|
||||
use std::io;
|
||||
use std::io::ErrorKind;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::net::UnixStream;
|
||||
use tokio::stream;
|
||||
use tokio::sync::mpsc::{self, UnboundedReceiver};
|
||||
|
||||
macro_rules! impl_sway_event_deserialize {
|
||||
($($enum_variant:ident => $struct_type:ident),*) => {
|
||||
$(
|
||||
impl SwayEventDeserialize for $struct_type {
|
||||
fn event_type() -> SwayEventType {
|
||||
SwayEventType::$enum_variant
|
||||
}
|
||||
|
||||
fn deserialize_event(value: Value) -> Option<Self> {
|
||||
serde_json::from_value(value).ok()
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
// Use the macro to implement SwayEventDeserialize for multiple event types
|
||||
impl_sway_event_deserialize!(
|
||||
Workspace => WorkspaceChange,
|
||||
Output => OutputChange,
|
||||
Mode => ModeChange,
|
||||
Window => WindowChange,
|
||||
BarconfigUpdate => BarConfigUpdate,
|
||||
Binding => BindingEvent,
|
||||
Shutdown => ShutdownEvent,
|
||||
Tick => TickEvent,
|
||||
BarStateUpdate => BarStateUpdate,
|
||||
Input => InputEvent
|
||||
);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SwayIpcError {
|
||||
#[error("IO error occurred: {0}")]
|
||||
IoError(#[from] io::Error),
|
||||
#[error("Failed to parse JSON: {0}")]
|
||||
JsonError(#[from] serde_json::Error),
|
||||
#[error("Received unexpected message format")]
|
||||
UnexpectedMessageFormat,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SwayMessageType {
|
||||
RunCommand = 0,
|
||||
GetWorkspaces = 1,
|
||||
Subscribe = 2,
|
||||
GetOutputs = 3,
|
||||
GetTree = 4,
|
||||
GetMarks = 5,
|
||||
GetBarConfig = 6,
|
||||
GetVersion = 7,
|
||||
GetBindingModes = 8,
|
||||
GetConfig = 9,
|
||||
SendTick = 10,
|
||||
Sync = 11,
|
||||
GetBindingState = 12,
|
||||
GetInputs = 100,
|
||||
GetSeats = 101,
|
||||
}
|
||||
|
||||
impl From<&SwayMessage> for SwayMessageType {
|
||||
fn from(message: &SwayMessage) -> Self {
|
||||
match message {
|
||||
SwayMessage::RunCommand { .. } => SwayMessageType::RunCommand,
|
||||
SwayMessage::GetWorkspaces => SwayMessageType::GetWorkspaces,
|
||||
SwayMessage::Subscribe { .. } => SwayMessageType::Subscribe,
|
||||
SwayMessage::GetOutputs => SwayMessageType::GetOutputs,
|
||||
SwayMessage::GetTree => SwayMessageType::GetTree,
|
||||
SwayMessage::GetMarks => SwayMessageType::GetMarks,
|
||||
SwayMessage::GetBarConfig(_) => SwayMessageType::GetBarConfig,
|
||||
SwayMessage::GetVersion => SwayMessageType::GetVersion,
|
||||
SwayMessage::GetBindingModes => SwayMessageType::GetBindingModes,
|
||||
SwayMessage::GetConfig => SwayMessageType::GetConfig,
|
||||
SwayMessage::SendTick(_) => SwayMessageType::SendTick,
|
||||
SwayMessage::Sync => SwayMessageType::Sync,
|
||||
SwayMessage::GetBindingState => SwayMessageType::GetBindingState,
|
||||
SwayMessage::GetInputs => SwayMessageType::GetInputs,
|
||||
SwayMessage::GetSeats => SwayMessageType::GetSeats,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SwayMessage {
|
||||
RunCommand(String),
|
||||
Subscribe(Vec<String>),
|
||||
GetBarConfig(Option<String>),
|
||||
SendTick(Option<String>),
|
||||
GetVersion,
|
||||
GetWorkspaces,
|
||||
GetOutputs,
|
||||
GetTree,
|
||||
GetMarks,
|
||||
GetBindingModes,
|
||||
GetConfig,
|
||||
Sync,
|
||||
GetBindingState,
|
||||
GetInputs,
|
||||
GetSeats,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SwayReply {
|
||||
pub success: bool,
|
||||
pub error: Option<String>,
|
||||
pub parse_error: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Workspace {
|
||||
pub num: i32,
|
||||
pub name: String,
|
||||
pub visible: bool,
|
||||
pub focused: bool,
|
||||
pub urgent: bool,
|
||||
pub rect: Rect,
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Rect {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Output {
|
||||
pub name: String,
|
||||
pub make: String,
|
||||
pub model: String,
|
||||
pub serial: String,
|
||||
pub active: bool,
|
||||
pub dpms: bool,
|
||||
pub power: bool,
|
||||
pub primary: bool,
|
||||
pub scale: f64,
|
||||
pub subpixel_hinting: String,
|
||||
pub transform: String,
|
||||
pub current_workspace: Option<String>,
|
||||
pub modes: Vec<OutputMode>,
|
||||
pub current_mode: OutputMode,
|
||||
pub rect: Rect,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OutputMode {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub refresh: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SwayVersion {
|
||||
pub major: i32,
|
||||
pub minor: i32,
|
||||
pub patch: i32,
|
||||
pub human_readable: String,
|
||||
pub loaded_config_file_name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct BarConfig {
|
||||
pub id: String,
|
||||
pub mode: String,
|
||||
pub position: String,
|
||||
pub status_command: Option<String>,
|
||||
pub font: Option<String>,
|
||||
pub workspace_buttons: bool,
|
||||
pub workspace_min_width: Option<i32>,
|
||||
pub binding_mode_indicator: bool,
|
||||
pub verbose: bool,
|
||||
pub colors: BarColors,
|
||||
pub gaps: BarGaps,
|
||||
pub bar_height: Option<i32>,
|
||||
pub status_padding: Option<i32>,
|
||||
pub status_edge_padding: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct BarColors {
|
||||
pub background: String,
|
||||
pub statusline: String,
|
||||
pub separator: String,
|
||||
pub focused_background: String,
|
||||
pub focused_statusline: String,
|
||||
pub focused_separator: String,
|
||||
pub focused_workspace_text: String,
|
||||
pub focused_workspace_bg: String,
|
||||
pub focused_workspace_border: String,
|
||||
pub active_workspace_text: String,
|
||||
pub active_workspace_bg: String,
|
||||
pub active_workspace_border: String,
|
||||
pub inactive_workspace_text: String,
|
||||
pub inactive_workspace_bg: String,
|
||||
pub inactive_workspace_border: String,
|
||||
pub urgent_workspace_text: String,
|
||||
pub urgent_workspace_bg: String,
|
||||
pub urgent_workspace_border: String,
|
||||
pub binding_mode_text: String,
|
||||
pub binding_mode_bg: String,
|
||||
pub binding_mode_border: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct BarGaps {
|
||||
pub top: i32,
|
||||
pub right: i32,
|
||||
pub bottom: i32,
|
||||
pub left: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct InputDevice {
|
||||
pub identifier: String,
|
||||
pub name: String,
|
||||
pub vendor: i32,
|
||||
pub product: i32,
|
||||
pub devtype: String,
|
||||
pub xkb_active_layout_name: Option<String>,
|
||||
pub xkb_layout_names: Option<Vec<String>>,
|
||||
pub xkb_active_layout_index: Option<i32>,
|
||||
pub libinput: Option<LibInput>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct LibInput {
|
||||
pub send_events: Option<String>,
|
||||
pub tap: Option<String>,
|
||||
pub tap_button_map: Option<String>,
|
||||
pub tap_drag: Option<String>,
|
||||
pub tap_drag_lock: Option<String>,
|
||||
pub accel_speed: Option<f64>,
|
||||
pub accel_profile: Option<String>,
|
||||
pub natural_scroll: Option<String>,
|
||||
pub left_handed: Option<String>,
|
||||
pub click_method: Option<String>,
|
||||
pub middle_emulation: Option<String>,
|
||||
pub scroll_method: Option<String>,
|
||||
pub scroll_button: Option<i32>,
|
||||
pub scroll_button_lock: Option<String>,
|
||||
pub dwt: Option<String>,
|
||||
pub dwtp: Option<String>,
|
||||
pub calibration_matrix: Option<Vec<f64>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Seat {
|
||||
pub name: String,
|
||||
pub capabilities: i32,
|
||||
pub focus: i64,
|
||||
pub devices: Vec<InputDevice>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SwayEventType {
|
||||
Workspace,
|
||||
Output,
|
||||
Mode,
|
||||
Window,
|
||||
BarconfigUpdate,
|
||||
Binding,
|
||||
Shutdown,
|
||||
Tick,
|
||||
BarStateUpdate,
|
||||
Input,
|
||||
}
|
||||
|
||||
impl Display for SwayEventType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
SwayEventType::Workspace => "workspace",
|
||||
SwayEventType::Output => "output",
|
||||
SwayEventType::Mode => "mode",
|
||||
SwayEventType::Window => "window",
|
||||
SwayEventType::BarconfigUpdate => "barconfig_update",
|
||||
SwayEventType::Binding => "binding",
|
||||
SwayEventType::Shutdown => "shutdown",
|
||||
SwayEventType::Tick => "tick",
|
||||
SwayEventType::BarStateUpdate => "bar_state_update",
|
||||
SwayEventType::Input => "input",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged, rename_all = "snake_case")]
|
||||
pub enum SwayEvent {
|
||||
Workspace(WorkspaceChange),
|
||||
Output(OutputChange),
|
||||
Mode(ModeChange),
|
||||
Window(WindowChange),
|
||||
BarconfigUpdate(BarConfigUpdate),
|
||||
Binding(BindingEvent),
|
||||
Shutdown(ShutdownEvent),
|
||||
Tick(TickEvent),
|
||||
BarStateUpdate(BarStateUpdate),
|
||||
Input(InputEvent),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct WorkspaceChange {
|
||||
pub change: WorkspaceChangeType,
|
||||
pub current: Option<Node>,
|
||||
pub old: Option<Node>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WorkspaceChangeType {
|
||||
Init,
|
||||
Empty,
|
||||
Focus,
|
||||
Move,
|
||||
Rename,
|
||||
Urgent,
|
||||
Reload,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum NodeType {
|
||||
Root,
|
||||
Output,
|
||||
Workspace,
|
||||
Con,
|
||||
FloatingCon,
|
||||
Dockarea, // i3-specific
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Node {
|
||||
pub id: i64,
|
||||
pub name: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
pub node_type: NodeType,
|
||||
pub rect: Rect,
|
||||
pub focused: bool,
|
||||
pub focus: Vec<i64>,
|
||||
pub border: String,
|
||||
pub current_border_width: i32,
|
||||
pub layout: String,
|
||||
pub orientation: String,
|
||||
pub percent: Option<f64>,
|
||||
pub window_rect: Rect,
|
||||
pub deco_rect: Rect,
|
||||
pub geometry: Rect,
|
||||
pub window: Option<i64>,
|
||||
pub urgent: Option<bool>,
|
||||
pub floating_nodes: Vec<Node>,
|
||||
pub sticky: bool,
|
||||
pub app_id: Option<String>,
|
||||
pub pid: Option<i32>,
|
||||
pub visible: Option<bool>,
|
||||
pub shell: Option<String>,
|
||||
pub inhibit_idle: Option<bool>,
|
||||
pub idle_inhibitors: Option<IdleInhibitors>,
|
||||
pub nodes: Vec<Node>,
|
||||
pub fullscreen_mode: Option<i32>,
|
||||
pub marks: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct IdleInhibitors {
|
||||
pub application: Option<String>,
|
||||
pub user: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ModeChange {
|
||||
pub change: String,
|
||||
pub pango_markup: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OutputChange {
|
||||
pub change: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct WindowChange {
|
||||
pub change: WindowChangeType,
|
||||
pub container: Node,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WindowChangeType {
|
||||
New,
|
||||
Close,
|
||||
Focus,
|
||||
Title,
|
||||
FullscreenMode,
|
||||
Move,
|
||||
Floating,
|
||||
Urgent,
|
||||
Mark,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct BarConfigUpdate {
|
||||
pub id: String,
|
||||
pub mode: String,
|
||||
pub position: String,
|
||||
pub status_command: String,
|
||||
pub font: String,
|
||||
pub workspace_buttons: bool,
|
||||
pub binding_mode_indicator: bool,
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct BindingEvent {
|
||||
pub change: String,
|
||||
pub binding: Binding,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Binding {
|
||||
pub command: String,
|
||||
pub event_state_mask: Vec<String>,
|
||||
pub input_code: i32,
|
||||
pub symbol: Option<String>,
|
||||
pub input_type: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ShutdownEvent {
|
||||
pub change: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct TickEvent {
|
||||
pub first: bool,
|
||||
pub payload: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct BarStateUpdate {
|
||||
pub id: String,
|
||||
pub visible_by_modifier: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct InputEvent {
|
||||
pub change: String,
|
||||
pub input: InputDevice,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SwayIpc {
|
||||
socket_path: String,
|
||||
}
|
||||
|
||||
// Define a trait for event deserialization
|
||||
pub trait SwayEventDeserialize: Sized {
|
||||
fn event_type() -> SwayEventType;
|
||||
fn deserialize_event(value: Value) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl SwayIpc {
|
||||
pub async fn connect() -> Result<Self, SwayIpcError> {
|
||||
let socket_path = std::env::var("SWAYSOCK").map_err(|_| {
|
||||
io::Error::new(
|
||||
ErrorKind::NotFound,
|
||||
"SWAYSOCK environment variable not found",
|
||||
)
|
||||
})?;
|
||||
Ok(SwayIpc { socket_path })
|
||||
}
|
||||
|
||||
async fn send_and_receive(&self, message: SwayMessage) -> Result<Value, SwayIpcError> {
|
||||
let mut stream = UnixStream::connect(&self.socket_path).await?;
|
||||
|
||||
let payload = match &message {
|
||||
SwayMessage::RunCommand(command) => command.to_owned(),
|
||||
_ => serde_json::to_string(&message)?,
|
||||
};
|
||||
|
||||
let magic_string = "i3-ipc".as_bytes();
|
||||
let payload_length = payload.len() as u32;
|
||||
let _payload_type: u32 = SwayMessageType::from(&message) as u32;
|
||||
|
||||
let mut message_buffer = vec![];
|
||||
message_buffer.extend_from_slice(magic_string);
|
||||
message_buffer.extend_from_slice(&payload_length.to_ne_bytes());
|
||||
message_buffer.extend_from_slice(&_payload_type.to_ne_bytes());
|
||||
message_buffer.extend_from_slice(payload.as_bytes());
|
||||
|
||||
stream.writable().await?;
|
||||
stream.try_write(&message_buffer)?;
|
||||
|
||||
stream.readable().await?;
|
||||
debug!("Reading response from the stream.");
|
||||
|
||||
// First, read the fixed-size header to determine the length of the payload
|
||||
let mut header = vec![0; 14];
|
||||
stream.try_read(&mut header)?;
|
||||
|
||||
if &header[0..6] != b"i3-ipc" {
|
||||
return Err(SwayIpcError::UnexpectedMessageFormat);
|
||||
}
|
||||
|
||||
let response_payload_length =
|
||||
u32::from_ne_bytes(header[6..10].try_into().unwrap()) as usize;
|
||||
|
||||
// Then, read the payload based on the length specified in the header
|
||||
let mut response_payload = vec![0; response_payload_length];
|
||||
stream.read_exact(&mut response_payload).await?;
|
||||
|
||||
let response: Value = SwayIpc::parse_response(&response_payload)?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn get_workspaces(&self) -> Result<Vec<Workspace>, SwayIpcError> {
|
||||
let response = self.send_and_receive(SwayMessage::GetWorkspaces).await?;
|
||||
let workspaces: Vec<Workspace> = serde_json::from_value(response)?;
|
||||
Ok(workspaces)
|
||||
}
|
||||
|
||||
pub async fn run_command(&self, command: String) -> Result<Vec<SwayReply>, SwayIpcError> {
|
||||
let response = self
|
||||
.send_and_receive(SwayMessage::RunCommand(command))
|
||||
.await?;
|
||||
let results: Vec<SwayReply> = serde_json::from_value(response)?;
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub async fn get_tree(&self) -> Result<Node, SwayIpcError> {
|
||||
let response = self.send_and_receive(SwayMessage::GetTree).await?;
|
||||
let tree: Node = serde_json::from_value(response)?;
|
||||
Ok(tree)
|
||||
}
|
||||
|
||||
pub async fn get_outputs(&self) -> Result<Node, SwayIpcError> {
|
||||
let response = self.send_and_receive(SwayMessage::GetOutputs).await?;
|
||||
let tree: Node = serde_json::from_value(response)?;
|
||||
Ok(tree)
|
||||
}
|
||||
|
||||
pub async fn get_version(&self) -> Result<SwayVersion, SwayIpcError> {
|
||||
let response = self.send_and_receive(SwayMessage::GetVersion).await?;
|
||||
let version: SwayVersion = serde_json::from_value(response)?;
|
||||
Ok(version)
|
||||
}
|
||||
|
||||
async fn subscribe_helper<F>(
|
||||
&self,
|
||||
event_names: Vec<String>,
|
||||
process_event: F,
|
||||
) -> Result<(), SwayIpcError>
|
||||
where
|
||||
F: Fn(Value, u32) + Send + 'static,
|
||||
{
|
||||
let socket_path = self.socket_path.clone();
|
||||
let stream = UnixStream::connect(&socket_path).await?;
|
||||
|
||||
let subscribe_message = SwayMessage::Subscribe(event_names);
|
||||
let payload = serde_json::to_string(&subscribe_message)?;
|
||||
|
||||
let mut buf = vec![0; 8192];
|
||||
Self::send_and_receive_internal(&stream, &payload, SwayMessageType::Subscribe, &mut buf)
|
||||
.await?;
|
||||
|
||||
if buf.is_empty() {
|
||||
return Err(SwayIpcError::UnexpectedMessageFormat);
|
||||
}
|
||||
|
||||
let json_value: Value = serde_json::from_slice(&buf[14..])?;
|
||||
if json_value.get("success") == Some(&Value::Bool(false)) {
|
||||
return Err(SwayIpcError::UnexpectedMessageFormat);
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
'outer: loop {
|
||||
match stream.readable().await {
|
||||
Ok(_) => {
|
||||
let mut event_buffer = vec![0; 8192];
|
||||
let mut received_data = vec![];
|
||||
|
||||
loop {
|
||||
match stream.try_read(&mut event_buffer) {
|
||||
Ok(n) if n > 0 => {
|
||||
received_data.extend_from_slice(&event_buffer[..n]);
|
||||
n
|
||||
}
|
||||
Ok(_) => {
|
||||
error!("Stream closed by peer");
|
||||
break 'outer;
|
||||
}
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
||||
Err(e) => {
|
||||
error!("Failed to read from stream: {:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if received_data.len() < 14 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if &received_data[0..6] != b"i3-ipc" {
|
||||
error!("Invalid magic string in event response");
|
||||
continue;
|
||||
}
|
||||
|
||||
let payload_length = u32::from_ne_bytes(
|
||||
received_data[6..10]
|
||||
.try_into()
|
||||
.expect("Invalid length bytes"),
|
||||
) as usize;
|
||||
|
||||
let payload_type = u32::from_ne_bytes(
|
||||
received_data[10..14]
|
||||
.try_into()
|
||||
.expect("Invalid length bytes"),
|
||||
);
|
||||
|
||||
if received_data.len() < 14 + payload_length {
|
||||
continue;
|
||||
}
|
||||
|
||||
let json_slice = &received_data[14..14 + payload_length];
|
||||
|
||||
match serde_json::from_slice(json_slice) {
|
||||
Ok(json_value) => {
|
||||
process_event(json_value, payload_type);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to parse event JSON: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
received_data.drain(0..14 + payload_length);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Stream not readable: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn subscribe<const N: usize>(
|
||||
&self,
|
||||
events: [SwayEventType; N],
|
||||
) -> Result<UnboundedReceiver<SwayEvent>, SwayIpcError> {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
let event_names: Vec<String> = events.iter().map(|e| e.to_string()).collect();
|
||||
|
||||
self.subscribe_helper(event_names, move |json_value, payload_type| {
|
||||
let event: Option<SwayEvent> = match payload_type {
|
||||
0x80000000 => serde_json::from_value(json_value)
|
||||
.map(SwayEvent::Workspace)
|
||||
.ok(),
|
||||
0x80000001 => serde_json::from_value(json_value)
|
||||
.map(SwayEvent::Output)
|
||||
.ok(),
|
||||
0x80000002 => serde_json::from_value(json_value).map(SwayEvent::Mode).ok(),
|
||||
0x80000003 => serde_json::from_value(json_value)
|
||||
.map(SwayEvent::Window)
|
||||
.ok(),
|
||||
0x80000004 => serde_json::from_value(json_value)
|
||||
.map(SwayEvent::BarconfigUpdate)
|
||||
.ok(),
|
||||
0x80000005 => serde_json::from_value(json_value)
|
||||
.map(SwayEvent::Binding)
|
||||
.ok(),
|
||||
0x80000006 => serde_json::from_value(json_value)
|
||||
.map(SwayEvent::Shutdown)
|
||||
.ok(),
|
||||
0x80000007 => serde_json::from_value(json_value).map(SwayEvent::Tick).ok(),
|
||||
0x80000014 => serde_json::from_value(json_value)
|
||||
.map(SwayEvent::BarStateUpdate)
|
||||
.ok(),
|
||||
0x80000015 => serde_json::from_value(json_value)
|
||||
.map(SwayEvent::Input)
|
||||
.ok(),
|
||||
_ => {
|
||||
error!("Unknown event type received: {:x}", payload_type);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(event) = event {
|
||||
if tx.send(event).is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(rx)
|
||||
}
|
||||
|
||||
pub async fn subscribe_typed<E: SwayEventDeserialize + Send + 'static>(
|
||||
&self,
|
||||
) -> Result<UnboundedReceiver<E>, SwayIpcError> {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
let event_name = E::event_type().to_string();
|
||||
|
||||
self.subscribe_helper(vec![event_name], move |json_value, payload_type| {
|
||||
if let Some(event) = E::deserialize_event(json_value) {
|
||||
if tx.send(event).is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(rx)
|
||||
}
|
||||
|
||||
async fn send_and_receive_internal(
|
||||
stream: &UnixStream,
|
||||
payload: &str,
|
||||
message_type: SwayMessageType,
|
||||
buf: &mut Vec<u8>,
|
||||
) -> Result<(), SwayIpcError> {
|
||||
let magic_string = "i3-ipc".as_bytes();
|
||||
let payload_length = payload.len() as u32;
|
||||
let payload_type: u32 = message_type as u32;
|
||||
|
||||
let mut message_buffer = vec![];
|
||||
message_buffer.extend_from_slice(magic_string);
|
||||
message_buffer.extend_from_slice(&payload_length.to_ne_bytes());
|
||||
message_buffer.extend_from_slice(&payload_type.to_ne_bytes());
|
||||
message_buffer.extend_from_slice(payload.as_bytes());
|
||||
|
||||
stream.writable().await?;
|
||||
stream.try_write(&message_buffer)?;
|
||||
|
||||
stream.readable().await?;
|
||||
buf.clear();
|
||||
buf.resize(8192, 0);
|
||||
|
||||
let n = stream.try_read(buf)?;
|
||||
buf.truncate(n);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_response(
|
||||
&self,
|
||||
buf: &mut Vec<u8>,
|
||||
stream: &UnixStream,
|
||||
) -> Result<usize, SwayIpcError> {
|
||||
let n = stream.try_read(buf)?;
|
||||
|
||||
if n == 0 {
|
||||
return Err(SwayIpcError::IoError(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
"Socket closed by peer",
|
||||
)));
|
||||
}
|
||||
|
||||
if !buf.starts_with(b"i3-ipc") {
|
||||
return Err(SwayIpcError::UnexpectedMessageFormat);
|
||||
}
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn parse_response(json_slice: &[u8]) -> Result<Value, SwayIpcError> {
|
||||
match std::str::from_utf8(json_slice) {
|
||||
Ok(json_str) => debug!("JSON response: {}", json_str),
|
||||
Err(e) => {
|
||||
return Err(SwayIpcError::IoError(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Failed to parse UTF-8 from response: {:?}", e),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
let response: Value = serde_json::from_slice(json_slice)?;
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_nodes_by_criteria<'a, F>(root: &'a Node, criteria: F) -> Vec<&'a Node>
|
||||
where
|
||||
F: Fn(&Node) -> bool,
|
||||
{
|
||||
let mut stack = vec![root];
|
||||
let mut collector = Vec::new();
|
||||
|
||||
while let Some(node) = stack.pop() {
|
||||
if criteria(node) {
|
||||
collector.push(node);
|
||||
}
|
||||
// Extend the stack with children in reverse order (right to left)
|
||||
for child in node.nodes.iter().rev() {
|
||||
stack.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
collector
|
||||
}
|
||||
|
||||
pub fn collect_leaves<'a>(node: &'a Node) -> Vec<&'a Node> {
|
||||
let criteria = |node: &Node| node.node_type == NodeType::Con && node.name.is_some();
|
||||
collect_nodes_by_criteria(node, criteria)
|
||||
}
|
||||
|
||||
// Tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
#[test]
|
||||
fn test_get_workspaces() {
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let sway_ipc = SwayIpc::connect().await.unwrap();
|
||||
let workspaces = sway_ipc.get_workspaces().await.unwrap();
|
||||
assert!(!workspaces.is_empty());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_command() {
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let sway_ipc = SwayIpc::connect().await.unwrap();
|
||||
let result = sway_ipc
|
||||
.run_command("focus left".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(result[0].success);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_version() {
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let sway_ipc = SwayIpc::connect().await.unwrap();
|
||||
let version = sway_ipc.get_version().await.unwrap();
|
||||
assert_eq!(version.major, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user