Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
291aa6877c
|
|||
|
c48a8adcdc
|
|||
|
1f1e7fbb53
|
|||
|
7ec268094d
|
|||
|
afa690366f
|
|||
|
02ba7f2684
|
|||
|
44903f2ce0
|
|||
|
9f50d61ad5
|
|||
|
788a01ce83
|
|||
|
f5451b6c2f
|
|||
|
2de18cdbdb
|
|||
|
23bfd0c4a7
|
|||
|
3342293bf2
|
|||
|
2b3ab7389d
|
|||
|
f80d36e962
|
|||
|
86edd4c5b3
|
|||
|
a7a8ceedeb
|
|||
|
7ce0d68021
|
|||
|
b5cb4677ee
|
|||
|
38faf99de7
|
|||
|
69f252bad8
|
|||
|
c177c089a3
|
|||
|
d964e7857b
|
|||
|
8430592bee
|
|||
|
78cfc09d60
|
|||
|
a7b764ad0f
|
|||
|
f3e6fe13df
|
|||
|
4f8f5db620
|
|||
|
1e39640508
|
|||
|
b388bc727b
|
|||
|
9f859e1df1
|
|||
|
4bb5702eba
|
|||
|
955fdbcff1
|
|||
|
322c8c8fc3
|
|||
|
93afaf1bde
|
|||
|
c915c67f08
|
|||
|
813ffac614
|
|||
|
b0bc2752cc
|
|||
|
07e9161137
|
|||
|
f2977f1ba9
|
|||
|
b0b4134829
|
|||
|
c1dbdd4644
|
|||
|
64c94d6e89
|
|||
|
4d11df5ad1
|
|||
|
0cacbe2229
|
|||
|
7bbe1c4ced
|
|||
|
83918fd432
|
|||
|
07e1c76aa9
|
|||
|
d84e653db2
|
|||
|
7834bbd956
|
|||
|
249ffb2a36
|
|||
|
fdd4880d05
|
|||
|
9ca6544057
|
|||
|
e5f70ca87a
|
|||
|
b03f60c985
|
|||
|
b22016e970
|
|||
|
65c7798d01
|
|||
|
5188809327
|
|||
|
a4276a2caa
|
|||
|
7b27a650a1
|
|||
|
062dbcafb8
|
|||
|
57a6b0a3ee
|
|||
|
b5fbaadca2
|
|||
|
3bd7aaaad2
|
|||
|
8aba3d8859
|
|||
|
e4ece7d6b2
|
|||
|
f07f90aee5
|
|||
|
6e2aa2ba65
|
|||
|
1ef8ef669a
|
|||
|
c6a123a6e1
|
|||
|
06e24f0ce0
|
|||
|
da31ab75e2
|
|||
|
d09ca013d5
|
|||
|
10913fd48c
|
|||
|
73ddb6d498
|
|||
|
ede28623ef
|
|||
|
130fe49597
|
|||
|
4a1df97ad6
|
|||
|
7a966051d5
|
|||
|
7dc3d7f9cf
|
|||
|
2b1e99445a
|
|||
|
e932b62195
|
|||
|
49f440770e
|
|||
|
153ae9520f
|
|||
|
424c530d5d
|
|||
|
a637f24e66
|
|||
|
1d693b7b2a
|
|||
|
484b1fb68d
|
|||
|
2fa58533ba
|
|||
|
088665c9ff
|
|||
|
3e2e3fdc68
|
|||
|
59994ce740
|
|||
|
b42cad0b52
|
|||
|
3c49ece1a9
|
|||
|
5a5870d7de
|
|||
|
e5303954c5
|
|||
|
c95d39a9ec
|
|||
|
e910d29aa4
|
|||
|
58a06bd930
|
|||
|
380a4aed2c
|
|||
|
ed7f9a6917
|
|||
|
432f16ae2b
|
|||
|
44ba3eb4dc
|
|||
|
d80ebfc4cf
|
|||
|
9f07114401
|
|||
|
5e0fe71feb
|
|||
|
366f56da9e
|
|||
|
9cef59eb88
|
|||
|
abacb54c8d
|
|||
|
86183e56ad
|
|||
|
6a9cd00275
|
|||
|
9cb92741a4
|
|||
|
cea3d3da04
|
|||
|
87fa93fb1c
|
|||
|
cf0db472e8
|
|||
|
4f6392e376
|
|||
|
2fb97a9964
|
|||
|
48beaa7732
|
|||
|
2bd53462d7
|
|||
|
edeb0e3b78
|
|||
|
2ee6bbc582
|
|||
|
3e512092bd
|
|||
|
08104b3537
|
|||
|
1561ae2e80
|
|||
|
d45502a43e
|
|||
|
4fada75fe9
|
|||
|
184f9fc215
|
|||
|
74074bf9c7
|
|||
|
84f061a79f
|
|||
|
472be6c083
|
|||
|
49e070a41d
|
|||
|
8293c6e6e5
|
@@ -1,2 +0,0 @@
|
||||
[registries]
|
||||
pvv-git = { index = "sparse+https://git.pvv.ntnu.no/api/packages/Grzegorz/cargo/" }
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: debian-latest
|
||||
runs-on: debian-latest-slim
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
run: cargo build --verbose --release
|
||||
|
||||
check:
|
||||
runs-on: debian-latest
|
||||
runs-on: debian-latest-slim
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
run: cargo clippy
|
||||
|
||||
test:
|
||||
runs-on: debian-latest
|
||||
runs-on: debian-latest-slim
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: cargo-bins/cargo-binstall@main
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
known-hosts: "pages.pvv.ntnu.no ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2QjfFB+city1SYqltkVqWACfo1j37k+oQQfj13mtgg"
|
||||
|
||||
docs:
|
||||
runs-on: debian-latest
|
||||
runs-on: debian-latest-slim
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
run: cargo doc --document-private-items --release
|
||||
|
||||
- name: Transfer files
|
||||
uses: https://git.pvv.ntnu.no/Projects/rsync-action@v2
|
||||
uses: https://git.pvv.ntnu.no/Projects/rsync-action@v1
|
||||
with:
|
||||
source: target/doc/
|
||||
target: ${{ gitea.ref_name }}/docs/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/target
|
||||
result
|
||||
result-*
|
||||
|
||||
Cargo.lock
|
||||
|
||||
Generated
-900
@@ -1,900 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891"
|
||||
dependencies = [
|
||||
"term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "empidee"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"futures-util",
|
||||
"indoc",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"paste",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop"
|
||||
version = "0.22.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501"
|
||||
dependencies = [
|
||||
"ascii-canvas",
|
||||
"bit-set",
|
||||
"ena",
|
||||
"itertools",
|
||||
"lalrpop-util",
|
||||
"petgraph",
|
||||
"pico-args",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"sha3",
|
||||
"string_cache",
|
||||
"term",
|
||||
"unicode-xid",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop-util"
|
||||
version = "0.22.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.184"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||
dependencies = [
|
||||
"diff",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
|
||||
dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"parking_lot",
|
||||
"phf_shared",
|
||||
"precomputed-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
+6
-7
@@ -4,7 +4,6 @@ version = "0.1.0"
|
||||
authors = [
|
||||
"Øystein Tveit <oysteikt@pvv.ntnu.no>"
|
||||
]
|
||||
license = "MIT"
|
||||
description = "A rust implementation of the mpd protocol, both client and serverside"
|
||||
repository = "https://git.pvv.ntnu.no/Grzegorz/empidee"
|
||||
documentation = "https://pages.pvv.ntnu.no/Grzegorz/empidee/main/docs/empidee/"
|
||||
@@ -12,13 +11,13 @@ edition = "2024"
|
||||
rust-version = "1.85.0"
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.44", features = ["serde"] }
|
||||
futures-util = { version = "0.3.32", optional = true, features = ["io"] }
|
||||
chrono = { version = "0.4.42", features = ["serde"] }
|
||||
futures-util = { version = "0.3.31", optional = true, features = ["io"] }
|
||||
lalrpop-util = { version = "0.22.2", features = ["lexer"] }
|
||||
paste = "1.0.15"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
thiserror = "2.0.18"
|
||||
tokio = { version = "1.50.0", optional = true, features = ["io-util"] }
|
||||
thiserror = "2.0.17"
|
||||
tokio = { version = "1.48.0", optional = true, features = ["io-util"] }
|
||||
|
||||
[features]
|
||||
default = ["tokio"]
|
||||
@@ -26,10 +25,10 @@ futures = ["dep:futures-util"]
|
||||
tokio = ["dep:tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.102"
|
||||
anyhow = "1.0.100"
|
||||
indoc = "2.0.7"
|
||||
pretty_assertions = "1.4.1"
|
||||
tokio = { version = "1.50.0", features = ["macros", "net", "rt"] }
|
||||
tokio = { version = "1.48.0", features = ["macros", "net", "rt"] }
|
||||
|
||||
[build-dependencies]
|
||||
lalrpop = "0.22.2"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Programvareverkstedet <projects@pvv.ntnu.no>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,9 +1,8 @@
|
||||
fn main() {
|
||||
lalrpop::process_root().unwrap();
|
||||
// let debug_mode = std::env::var("PROFILE").unwrap() == "debug";
|
||||
|
||||
lalrpop::Configuration::new()
|
||||
//.emit_comments(debug_mode)
|
||||
// .generate_in_source_tree()
|
||||
.process()
|
||||
.unwrap();
|
||||
// lalrpop::Configuration::new()
|
||||
// .emit_comments(debug_mode)
|
||||
// .process()
|
||||
// .unwrap();
|
||||
}
|
||||
|
||||
@@ -4,11 +4,9 @@ use empidee::MpdClient;
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let socket = tokio::net::TcpSocket::new_v4()?;
|
||||
let mut stream = socket.connect("127.0.0.1:6600".parse()?).await?;
|
||||
let mut client = MpdClient::new(&mut stream).await?;
|
||||
println!(
|
||||
"Connected to MPD server: {}",
|
||||
client.get_mpd_version().unwrap_or("unknown")
|
||||
);
|
||||
|
||||
let mut client = MpdClient::new(&mut stream);
|
||||
println!("{}", client.read_initial_mpd_version().await?);
|
||||
|
||||
client.play(None).await?;
|
||||
|
||||
|
||||
Generated
+6
-6
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1775036866,
|
||||
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
|
||||
"lastModified": 1764950072,
|
||||
"narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
|
||||
"rev": "f61125a668a320878494449750330ca58b78c557",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -29,11 +29,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775099554,
|
||||
"narHash": "sha256-3xBsGnGDLOFtnPZ1D3j2LU19wpAlYefRKTlkv648rU0=",
|
||||
"lastModified": 1765075567,
|
||||
"narHash": "sha256-KFDCdQcHJ0hE3Nt5Gm5enRIhmtEifAjpxgUQ3mzSJpA=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "8d6387ed6d8e6e6672fd3ed4b61b59d44b124d99",
|
||||
"rev": "769156779b41e8787a46ca3d7d76443aaf68be6f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -42,46 +42,5 @@
|
||||
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
|
||||
};
|
||||
});
|
||||
|
||||
packages = forAllSystems (system: pkgs: toolchain: let
|
||||
rustPlatform = pkgs.makeRustPlatform {
|
||||
cargo = toolchain;
|
||||
rustc = toolchain;
|
||||
};
|
||||
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
||||
src = lib.fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = lib.fileset.unions [
|
||||
./.cargo
|
||||
./Cargo.toml
|
||||
./Cargo.lock
|
||||
./LICENSE
|
||||
./README.md
|
||||
./build.rs
|
||||
./examples
|
||||
./rustfmt.toml
|
||||
./src
|
||||
];
|
||||
};
|
||||
in {
|
||||
default = self.packages.${system}.example-binaries;
|
||||
example-binaries = rustPlatform.buildRustPackage {
|
||||
pname = "empidee-example-bins";
|
||||
inherit (cargoToml.package) version;
|
||||
inherit src;
|
||||
|
||||
cargoLock.lockFile = ./Cargo.lock;
|
||||
|
||||
cargoBuildFlags = [ "--examples" ];
|
||||
|
||||
# TODO: avoid the binary variant with the hash at the end
|
||||
postInstall = ''
|
||||
find "$releaseDir"/examples -type f -executable -exec install -Dt "$out/bin" {} \;
|
||||
'';
|
||||
|
||||
doCheck = true;
|
||||
useNextest = true;
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
+22
-55
@@ -4,25 +4,23 @@
|
||||
//! managing the playlist, and returns the expected response types directly
|
||||
//! from its methods.
|
||||
|
||||
use crate::{commands::*, types::SongPosition};
|
||||
use crate::{Request, commands::*, types::SongPosition};
|
||||
|
||||
#[cfg(feature = "futures")]
|
||||
use futures_util::{
|
||||
AsyncBufReadExt,
|
||||
io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter},
|
||||
io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader},
|
||||
};
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufStream};
|
||||
|
||||
use thiserror::Error;
|
||||
#[cfg(feature = "tokio")]
|
||||
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
|
||||
|
||||
pub struct MpdClient<'a, T>
|
||||
where
|
||||
T: AsyncWrite + AsyncRead + Unpin,
|
||||
{
|
||||
stream: BufStream<&'a mut T>,
|
||||
mpd_version: Option<String>,
|
||||
connection: &'a mut T,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -41,53 +39,30 @@ impl<'a, T> MpdClient<'a, T>
|
||||
where
|
||||
T: AsyncWrite + AsyncRead + Unpin,
|
||||
{
|
||||
pub async fn new(connection: &'a mut T) -> Result<Self, MpdClientError> {
|
||||
let mut client = MpdClient {
|
||||
stream: BufStream::new(connection),
|
||||
mpd_version: None,
|
||||
};
|
||||
|
||||
client.read_initial_mpd_version().await?;
|
||||
|
||||
Ok(client)
|
||||
pub fn new(connection: &'a mut T) -> Self {
|
||||
MpdClient { connection }
|
||||
}
|
||||
|
||||
pub async fn wrap_existing(connection: &'a mut T, mpd_version: Option<String>) -> Self {
|
||||
MpdClient {
|
||||
stream: BufStream::new(connection),
|
||||
mpd_version,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_connection(self) -> &'a mut T {
|
||||
self.stream.into_inner()
|
||||
}
|
||||
|
||||
pub fn get_mpd_version(&self) -> Option<&str> {
|
||||
self.mpd_version.as_deref()
|
||||
}
|
||||
|
||||
async fn read_initial_mpd_version(&mut self) -> Result<(), MpdClientError> {
|
||||
pub async fn read_initial_mpd_version(&mut self) -> Result<String, MpdClientError> {
|
||||
let mut reader = BufReader::new(&mut self.connection);
|
||||
let mut version_line = String::new();
|
||||
|
||||
self.stream
|
||||
reader
|
||||
.read_line(&mut version_line)
|
||||
.await
|
||||
.map_err(MpdClientError::ConnectionError)?;
|
||||
|
||||
self.mpd_version = Some(version_line.trim().to_string());
|
||||
|
||||
Ok(())
|
||||
Ok(version_line.trim().to_string())
|
||||
}
|
||||
|
||||
async fn read_response(&mut self) -> Result<Vec<u8>, MpdClientError> {
|
||||
let mut response = Vec::new();
|
||||
let mut reader = BufReader::new(&mut self.connection);
|
||||
|
||||
loop {
|
||||
let mut line = Vec::new();
|
||||
|
||||
let bytes_read = self
|
||||
.stream
|
||||
let bytes_read = reader
|
||||
.read_until(b'\n', &mut line)
|
||||
.await
|
||||
.map_err(MpdClientError::ConnectionError)?;
|
||||
@@ -106,33 +81,25 @@ where
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn execute<C>(&mut self, request: C::Request) -> Result<C::Response, MpdClientError>
|
||||
where
|
||||
C: Command,
|
||||
{
|
||||
let payload = request.serialize();
|
||||
pub async fn play(
|
||||
&mut self,
|
||||
position: Option<SongPosition>,
|
||||
) -> Result<PlayResponse, MpdClientError> {
|
||||
let message = Request::Play(position);
|
||||
let payload = message.serialize();
|
||||
|
||||
self.stream
|
||||
self.connection
|
||||
.write_all(payload.as_bytes())
|
||||
.await
|
||||
.map_err(MpdClientError::ConnectionError)?;
|
||||
|
||||
self.stream
|
||||
self.connection
|
||||
.flush()
|
||||
.await
|
||||
.map_err(MpdClientError::ConnectionError)?;
|
||||
|
||||
let response_bytes = self.read_response().await?;
|
||||
let response = C::Response::parse_raw(&response_bytes)?;
|
||||
|
||||
let response = PlayResponse::parse_raw(&response_bytes)?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn play(
|
||||
&mut self,
|
||||
position: Option<SongPosition>,
|
||||
) -> Result<PlayResponse, MpdClientError> {
|
||||
let request = <Play as Command>::Request::new(position);
|
||||
self.execute::<Play>(request).await
|
||||
}
|
||||
}
|
||||
|
||||
+179
-68
@@ -9,9 +9,6 @@
|
||||
|
||||
use crate::{request_tokenizer::RequestTokenizer, response_tokenizer::ResponseAttributes};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
mod audio_output_devices;
|
||||
mod client_to_client;
|
||||
mod connection_settings;
|
||||
@@ -37,11 +34,22 @@ pub use playback_options::*;
|
||||
pub use querying_mpd_status::*;
|
||||
pub use queue::*;
|
||||
pub use reflection::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use stickers::*;
|
||||
pub use stored_playlists::*;
|
||||
|
||||
#[cfg(feature = "futures")]
|
||||
use futures_util::{
|
||||
AsyncBufReadExt,
|
||||
io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader},
|
||||
};
|
||||
|
||||
use thiserror::Error;
|
||||
#[cfg(feature = "tokio")]
|
||||
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
|
||||
|
||||
/// A trait modelling a single MPD command request.
|
||||
pub trait CommandRequest
|
||||
pub(crate) trait CommandRequest
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@@ -88,6 +96,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this specific request type to it's corresponding variant in the generic Request enum.
|
||||
fn into_request_enum(self) -> crate::Request;
|
||||
|
||||
/// Converts from the generic Request enum to this specific request type.
|
||||
///
|
||||
/// If the enum variant does not match this type, returns None.
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self>;
|
||||
|
||||
/// Serializes the request into a String.
|
||||
fn serialize(&self) -> String;
|
||||
|
||||
@@ -100,7 +116,7 @@ where
|
||||
/// This assumes the raw string starts with the command name, e.g.
|
||||
/// `command_name arg1 "arg2 arg3"`
|
||||
fn parse_raw(raw: &str) -> Result<Self, RequestParserError> {
|
||||
let (line, _rest) = raw
|
||||
let (line, rest) = raw
|
||||
.split_once('\n')
|
||||
.ok_or(RequestParserError::MissingNewline)?;
|
||||
|
||||
@@ -129,10 +145,18 @@ where
|
||||
}
|
||||
|
||||
/// A trait modelling a single MPD command response.
|
||||
pub trait CommandResponse
|
||||
pub(crate) trait CommandResponse
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
/// Converts this specific response type to it's corresponding variant in the generic Response enum.
|
||||
fn into_response_enum(self) -> crate::Response;
|
||||
|
||||
/// Converts from the generic Response enum to this specific response type.
|
||||
///
|
||||
/// If the enum variant does not match this type, returns None.
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self>;
|
||||
|
||||
// /// Serializes the response into a Vec<u8>.
|
||||
// fn serialize(&self) -> Vec<u8>;
|
||||
|
||||
@@ -156,6 +180,16 @@ pub trait Command {
|
||||
/// The command name used within the protocol
|
||||
const COMMAND: &'static str = Self::Request::COMMAND;
|
||||
|
||||
/// Serialize the request into a string.
|
||||
/// This should optimally produce an input that can be parsed by [`parse_request`]
|
||||
fn serialize_request(&self, request: Self::Request) -> String {
|
||||
request.serialize().to_owned()
|
||||
}
|
||||
/// Serialize the request into a bytestring.
|
||||
fn serialize_request_to_bytes(&self, request: Self::Request) -> Vec<u8> {
|
||||
self.serialize_request(request).into_bytes()
|
||||
}
|
||||
|
||||
/// Parse the request from its tokenized parts. See also [`parse_raw_request`].
|
||||
fn parse_request(parts: RequestTokenizer) -> Result<Self::Request, RequestParserError> {
|
||||
Self::Request::parse(parts)
|
||||
@@ -178,6 +212,55 @@ pub trait Command {
|
||||
fn parse_raw_response(raw: &[u8]) -> Result<Self::Response, ResponseParserError> {
|
||||
Self::Response::parse_raw(raw)
|
||||
}
|
||||
|
||||
async fn execute<T>(
|
||||
request: Self::Request,
|
||||
connection: &mut T,
|
||||
) -> Result<Self::Response, crate::MpdClientError>
|
||||
where
|
||||
Self: Sized,
|
||||
T: AsyncWrite + AsyncRead + Unpin,
|
||||
{
|
||||
let payload = request.serialize();
|
||||
|
||||
connection
|
||||
.write_all(payload.as_bytes())
|
||||
.await
|
||||
.map_err(crate::MpdClientError::ConnectionError)?;
|
||||
|
||||
connection
|
||||
.flush()
|
||||
.await
|
||||
.map_err(crate::MpdClientError::ConnectionError)?;
|
||||
|
||||
let mut response_bytes = Vec::new();
|
||||
let mut reader = BufReader::new(connection);
|
||||
|
||||
loop {
|
||||
let mut line = Vec::new();
|
||||
|
||||
let bytes_read = reader
|
||||
.read_until(b'\n', &mut line)
|
||||
.await
|
||||
.map_err(crate::MpdClientError::ConnectionError)?;
|
||||
|
||||
if bytes_read == 0 {
|
||||
break; // EOF reached
|
||||
}
|
||||
|
||||
response_bytes.extend_from_slice(&line);
|
||||
|
||||
// TODO: handle errors properly
|
||||
if line == b"OK\n" || line.starts_with(b"ACK ") {
|
||||
break; // End of response
|
||||
}
|
||||
}
|
||||
|
||||
let response = Self::parse_raw_response(&response_bytes)
|
||||
.map_err(crate::MpdClientError::ResponseParseError)?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
// Request/response implementation helpers
|
||||
@@ -189,15 +272,9 @@ macro_rules! empty_command_request {
|
||||
pub struct [<$name Request>];
|
||||
}
|
||||
|
||||
impl std::default::Default for paste::paste! { [<$name Request>] } {
|
||||
fn default() -> Self {
|
||||
paste::paste! { [<$name Request>] }
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn req() -> <Self as crate::commands::Command>::Request {
|
||||
paste::paste! { [<$name Request>] }
|
||||
impl paste::paste! { [<$name Request>] } {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +283,22 @@ macro_rules! empty_command_request {
|
||||
const MIN_ARGS: u32 = 0;
|
||||
const MAX_ARGS: Option<u32> = Some(0);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
match Self::COMMAND {
|
||||
$command_name => crate::Request::$name,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match (Self::COMMAND, request) {
|
||||
($command_name, crate::Request::$name) => {
|
||||
Some(paste::paste! { [<$name Request>] })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
Self::COMMAND.to_string() + "\n"
|
||||
}
|
||||
@@ -228,19 +321,21 @@ macro_rules! empty_command_response {
|
||||
pub struct [<$name Response>];
|
||||
}
|
||||
|
||||
impl std::default::Default for paste::paste! { [<$name Response>] } {
|
||||
fn default() -> Self {
|
||||
paste::paste! { [<$name Response>] }
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn res() -> <Self as crate::commands::Command>::Response {
|
||||
paste::paste! { [<$name Response>] }
|
||||
impl paste::paste! { [<$name Response>] } {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(_response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(
|
||||
_parts: crate::commands::ResponseAttributes<'_>,
|
||||
) -> Result<Self, crate::commands::ResponseParserError> {
|
||||
@@ -255,20 +350,12 @@ macro_rules! single_item_command_request {
|
||||
($name:ident, $command_name:expr, $item_type:ty) => {
|
||||
paste::paste! {
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct [<$name Request>] (pub $item_type);
|
||||
pub struct [<$name Request>] ($item_type);
|
||||
}
|
||||
|
||||
impl paste::paste! { [<$name Request>] } {
|
||||
pub fn new(item: $item_type) -> Self {
|
||||
paste::paste! {
|
||||
crate::commands::[<$name Request>](item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn req(item: $item_type) -> <Self as crate::commands::Command>::Request {
|
||||
<Self as crate::commands::Command>::Request::new(item)
|
||||
pub fn new(arg: $item_type) -> Self {
|
||||
Self(arg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +364,22 @@ macro_rules! single_item_command_request {
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(1);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
match Self::COMMAND {
|
||||
$command_name => crate::Request::$name(self.0),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match (Self::COMMAND, request) {
|
||||
($command_name, crate::Request::$name(item)) => {
|
||||
Some(paste::paste! { [<$name Request>] ( item ) })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {}\n", Self::COMMAND, self.0)
|
||||
}
|
||||
@@ -306,20 +409,12 @@ macro_rules! single_optional_item_command_request {
|
||||
($name:ident, $command_name:expr, $item_type:ty) => {
|
||||
paste::paste! {
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct [<$name Request>] (pub Option<$item_type>);
|
||||
pub struct [<$name Request>] (Option<$item_type>);
|
||||
}
|
||||
|
||||
impl paste::paste! { [<$name Request>] } {
|
||||
pub fn new(item: Option<$item_type>) -> Self {
|
||||
paste::paste! {
|
||||
crate::commands::[<$name Request>](item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn req(item: Option<$item_type>) -> <Self as crate::commands::Command>::Request {
|
||||
<Self as crate::commands::Command>::Request::new(item)
|
||||
pub fn new(arg: Option<$item_type>) -> Self {
|
||||
Self(arg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,6 +423,22 @@ macro_rules! single_optional_item_command_request {
|
||||
const MIN_ARGS: u32 = 0;
|
||||
const MAX_ARGS: Option<u32> = Some(1);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
match Self::COMMAND {
|
||||
$command_name => crate::Request::$name(self.0),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match (Self::COMMAND, request) {
|
||||
($command_name, crate::Request::$name(item)) => {
|
||||
Some(paste::paste! { [<$name Request>] ( item ) })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match &self.0 {
|
||||
Some(item) => format!("{} {}\n", Self::COMMAND, item),
|
||||
@@ -363,24 +474,24 @@ macro_rules! single_item_command_response {
|
||||
($name:ident, $item_name:expr, $item_type:ty) => {
|
||||
paste::paste! {
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct [<$name Response>] (pub $item_type);
|
||||
pub struct [<$name Response>] ( $item_type );
|
||||
}
|
||||
|
||||
impl paste::paste! { [<$name Response>] } {
|
||||
pub fn new(item: $item_type) -> Self {
|
||||
paste::paste! {
|
||||
crate::commands::[<$name Response>](item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn res(item: $item_type) -> <Self as crate::commands::Command>::Response {
|
||||
<Self as crate::commands::Command>::Response::new(item)
|
||||
pub fn new(arg: $item_type) -> Self {
|
||||
Self(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(_response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(
|
||||
parts: crate::commands::ResponseAttributes<'_>,
|
||||
) -> Result<Self, crate::commands::ResponseParserError> {
|
||||
@@ -413,24 +524,24 @@ macro_rules! multi_item_command_response {
|
||||
($name:ident, $item_name:expr, $item_type:ty) => {
|
||||
paste::paste! {
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct [<$name Response>] (pub Vec<$item_type>);
|
||||
pub struct [<$name Response>] ( Vec<$item_type> );
|
||||
}
|
||||
|
||||
impl paste::paste! { [<$name Response>] } {
|
||||
pub fn new(items: Vec<$item_type>) -> Self {
|
||||
paste::paste! {
|
||||
crate::commands::[<$name Response>](items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn res(items: Vec<$item_type>) -> <Self as crate::commands::Command>::Response {
|
||||
<Self as crate::commands::Command>::Response::new(items)
|
||||
pub fn new(arg: Vec<$item_type>) -> Self {
|
||||
Self(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(_response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(
|
||||
parts: crate::commands::ResponseAttributes<'_>,
|
||||
) -> Result<Self, crate::commands::ResponseParserError> {
|
||||
|
||||
@@ -15,12 +15,6 @@ empty_command_request!(Outputs, "outputs");
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct OutputsResponse(Vec<Output>);
|
||||
|
||||
impl OutputsResponse {
|
||||
pub fn new(outputs: Vec<Output>) -> Self {
|
||||
OutputsResponse(outputs)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Output {
|
||||
pub id: AudioOutputId,
|
||||
@@ -30,25 +24,15 @@ pub struct Output {
|
||||
pub attribute: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
pub fn new(
|
||||
id: AudioOutputId,
|
||||
name: String,
|
||||
plugin: String,
|
||||
enabled: bool,
|
||||
attribute: HashMap<String, String>,
|
||||
) -> Self {
|
||||
Output {
|
||||
id,
|
||||
name,
|
||||
plugin,
|
||||
enabled,
|
||||
attribute,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for OutputsResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts = parts.into_vec()?;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ pub struct OutputSetRequest {
|
||||
}
|
||||
|
||||
impl OutputSetRequest {
|
||||
pub fn new(output_id: AudioOutputId, attribute_name: String, attribute_value: String) -> Self {
|
||||
fn new(output_id: AudioOutputId, attribute_name: String, attribute_value: String) -> Self {
|
||||
Self {
|
||||
output_id,
|
||||
attribute_name,
|
||||
@@ -30,6 +30,23 @@ impl CommandRequest for OutputSetRequest {
|
||||
const MIN_ARGS: u32 = 3;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::OutputSet(self.output_id, self.attribute_name, self.attribute_value)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::OutputSet(output_id, attribute_name, attribute_value) => {
|
||||
Some(OutputSetRequest {
|
||||
output_id,
|
||||
attribute_name,
|
||||
attribute_value,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {} {} {}",
|
||||
|
||||
@@ -15,13 +15,15 @@ pub struct ChannelsResponse {
|
||||
pub channels: Vec<ChannelName>,
|
||||
}
|
||||
|
||||
impl ChannelsResponse {
|
||||
pub fn new(channels: Vec<ChannelName>) -> Self {
|
||||
ChannelsResponse { channels }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ChannelsResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
let mut channel_names = Vec::with_capacity(parts.len());
|
||||
|
||||
@@ -13,25 +13,21 @@ empty_command_request!(ReadMessages, "readmessages");
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ReadMessagesResponse(Vec<ReadMessagesResponseEntry>);
|
||||
|
||||
impl ReadMessagesResponse {
|
||||
pub fn new(entries: Vec<ReadMessagesResponseEntry>) -> Self {
|
||||
ReadMessagesResponse(entries)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ReadMessagesResponseEntry {
|
||||
channel: ChannelName,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl ReadMessagesResponseEntry {
|
||||
pub fn new(channel: ChannelName, message: String) -> Self {
|
||||
ReadMessagesResponseEntry { channel, message }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ReadMessagesResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
debug_assert!(parts.len() % 2 == 0);
|
||||
|
||||
@@ -15,7 +15,7 @@ pub struct SendMessageRequest {
|
||||
}
|
||||
|
||||
impl SendMessageRequest {
|
||||
pub fn new(channel: ChannelName, message: String) -> Self {
|
||||
fn new(channel: ChannelName, message: String) -> Self {
|
||||
Self { channel, message }
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,19 @@ impl CommandRequest for SendMessageRequest {
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = None;
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::SendMessage(self.channel, self.message)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::SendMessage(channel, message) => {
|
||||
Some(SendMessageRequest { channel, message })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}", Self::COMMAND, self.channel, self.message)
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ pub struct ProtocolDisable;
|
||||
pub struct ProtocolDisableRequest(Vec<Feature>);
|
||||
|
||||
impl ProtocolDisableRequest {
|
||||
pub fn new(features: Vec<Feature>) -> Self {
|
||||
ProtocolDisableRequest(features)
|
||||
fn new(features: Vec<Feature>) -> Self {
|
||||
Self(features)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,17 @@ impl CommandRequest for ProtocolDisableRequest {
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = None;
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::ProtocolDisable(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::ProtocolDisable(features) => Some(ProtocolDisableRequest(features)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let features = self
|
||||
.0
|
||||
|
||||
@@ -9,8 +9,8 @@ pub struct ProtocolEnable;
|
||||
pub struct ProtocolEnableRequest(Vec<Feature>);
|
||||
|
||||
impl ProtocolEnableRequest {
|
||||
pub fn new(features: Vec<Feature>) -> Self {
|
||||
ProtocolEnableRequest(features)
|
||||
fn new(features: Vec<Feature>) -> Self {
|
||||
Self(features)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,17 @@ impl CommandRequest for ProtocolEnableRequest {
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = None;
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::ProtocolEnable(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::ProtocolEnable(features) => Some(ProtocolEnableRequest(features)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let features = self
|
||||
.0
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::u32;
|
||||
|
||||
use crate::{
|
||||
commands::{Command, CommandRequest, RequestParserError, empty_command_response},
|
||||
request_tokenizer::RequestTokenizer,
|
||||
@@ -9,8 +11,8 @@ pub struct TagTypesDisable;
|
||||
pub struct TagTypesDisableRequest(Vec<TagName>);
|
||||
|
||||
impl TagTypesDisableRequest {
|
||||
pub fn new(tag_types: Vec<TagName>) -> Self {
|
||||
TagTypesDisableRequest(tag_types)
|
||||
fn new(tagnames: Vec<TagName>) -> Self {
|
||||
Self(tagnames)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +21,17 @@ impl CommandRequest for TagTypesDisableRequest {
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = None;
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::TagTypesDisable(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::TagTypesDisable(req) => Some(TagTypesDisableRequest(req)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {}",
|
||||
|
||||
@@ -9,8 +9,8 @@ pub struct TagTypesEnable;
|
||||
pub struct TagTypesEnableRequest(Vec<TagName>);
|
||||
|
||||
impl TagTypesEnableRequest {
|
||||
pub fn new(tag_types: Vec<TagName>) -> Self {
|
||||
TagTypesEnableRequest(tag_types)
|
||||
fn new(tagnames: Vec<TagName>) -> Self {
|
||||
Self(tagnames)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,17 @@ impl CommandRequest for TagTypesEnableRequest {
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = None;
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::TagTypesEnable(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::TagTypesEnable(req) => Some(TagTypesEnableRequest(req)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {}",
|
||||
|
||||
@@ -9,8 +9,8 @@ pub struct TagTypesReset;
|
||||
pub struct TagTypesResetRequest(Vec<TagName>);
|
||||
|
||||
impl TagTypesResetRequest {
|
||||
pub fn new(tag_types: Vec<TagName>) -> Self {
|
||||
TagTypesResetRequest(tag_types)
|
||||
fn new(tagnames: Vec<TagName>) -> Self {
|
||||
Self(tagnames)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,17 @@ impl CommandRequest for TagTypesResetRequest {
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = None;
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::TagTypesReset(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::TagTypesReset(req) => Some(TagTypesResetRequest(req)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {}",
|
||||
|
||||
@@ -8,8 +8,8 @@ pub struct Pause;
|
||||
pub struct PauseRequest(Option<bool>);
|
||||
|
||||
impl PauseRequest {
|
||||
pub fn new(state: Option<bool>) -> Self {
|
||||
PauseRequest(state)
|
||||
fn new(toggle: Option<bool>) -> Self {
|
||||
Self(toggle)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,17 @@ impl CommandRequest for PauseRequest {
|
||||
const MIN_ARGS: u32 = 0;
|
||||
const MAX_ARGS: Option<u32> = Some(1);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Pause(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Pause(value) => Some(PauseRequest(value)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match self.0 {
|
||||
Some(true) => format!("{} 1\n", Self::COMMAND),
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct SeekRequest {
|
||||
pub time: TimeWithFractions,
|
||||
}
|
||||
|
||||
impl SeekRequest {
|
||||
pub fn new(songpos: SongPosition, time: TimeWithFractions) -> Self {
|
||||
Self { songpos, time }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SeekRequest {
|
||||
const COMMAND: &'static str = "seek";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Seek(self.songpos, self.time)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Seek(songpos, time) => Some(SeekRequest { songpos, time }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.songpos, self.time)
|
||||
}
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct SeekCurRequest {
|
||||
pub time: TimeWithFractions,
|
||||
}
|
||||
|
||||
impl SeekCurRequest {
|
||||
pub fn new(mode: SeekMode, time: TimeWithFractions) -> Self {
|
||||
Self { mode, time }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SeekCurRequest {
|
||||
const COMMAND: &'static str = "seekcur";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(1);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::SeekCur(self.mode, self.time)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::SeekCur(mode, time) => Some(SeekCurRequest { mode, time }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let time_str = match self.mode {
|
||||
SeekMode::Absolute => format!("{}", self.time),
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct SeekIdRequest {
|
||||
pub time: TimeWithFractions,
|
||||
}
|
||||
|
||||
impl SeekIdRequest {
|
||||
pub fn new(songid: SongId, time: TimeWithFractions) -> Self {
|
||||
Self { songid, time }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SeekIdRequest {
|
||||
const COMMAND: &'static str = "seekid";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::SeekId(self.songid, self.time)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::SeekId(songid, time) => Some(SeekIdRequest { songid, time }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.songid, self.time)
|
||||
}
|
||||
|
||||
@@ -14,13 +14,15 @@ empty_command_request!(ListNeighbors, "listneighbors");
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ListNeighborsResponse(HashMap<String, String>);
|
||||
|
||||
impl ListNeighborsResponse {
|
||||
pub fn new(map: HashMap<String, String>) -> Self {
|
||||
ListNeighborsResponse(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ListNeighborsResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
debug_assert!(parts.len() % 2 == 0);
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct MountRequest {
|
||||
pub uri: Uri,
|
||||
}
|
||||
|
||||
impl MountRequest {
|
||||
pub fn new(path: MountPath, uri: Uri) -> Self {
|
||||
MountRequest { path, uri }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for MountRequest {
|
||||
const COMMAND: &'static str = "mount";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Mount(self.path, self.uri)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Mount(path, uri) => Some(MountRequest { path, uri }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
debug_assert!(self.path.to_str().is_some());
|
||||
format!(
|
||||
|
||||
@@ -10,17 +10,22 @@ pub struct Unmount;
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct UnmountRequest(MountPath);
|
||||
|
||||
impl UnmountRequest {
|
||||
pub fn new(path: MountPath) -> Self {
|
||||
UnmountRequest(path)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for UnmountRequest {
|
||||
const COMMAND: &'static str = "unmount";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(1);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Unmount(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Unmount(item) => Some(UnmountRequest(item)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
debug_assert!(self.0.to_str().is_some());
|
||||
format!(
|
||||
|
||||
@@ -17,17 +17,22 @@ pub struct AlbumArtRequest {
|
||||
offset: Offset,
|
||||
}
|
||||
|
||||
impl AlbumArtRequest {
|
||||
pub fn new(uri: Uri, offset: Offset) -> Self {
|
||||
AlbumArtRequest { uri, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for AlbumArtRequest {
|
||||
const COMMAND: &'static str = "albumart";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::AlbumArt(self.uri, self.offset)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::AlbumArt(uri, offset) => Some(AlbumArtRequest { uri, offset }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.uri, self.offset)
|
||||
}
|
||||
@@ -64,13 +69,15 @@ pub struct AlbumArtResponse {
|
||||
pub binary: Vec<u8>,
|
||||
}
|
||||
|
||||
impl AlbumArtResponse {
|
||||
pub fn new(size: usize, binary: Vec<u8>) -> Self {
|
||||
AlbumArtResponse { size, binary }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for AlbumArtResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -18,17 +18,22 @@ pub struct CountRequest {
|
||||
group: Option<GroupType>,
|
||||
}
|
||||
|
||||
impl CountRequest {
|
||||
pub fn new(filter: Filter, group: Option<GroupType>) -> Self {
|
||||
CountRequest { filter, group }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for CountRequest {
|
||||
const COMMAND: &'static str = "count";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Count(self.filter, self.group)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Count(filter, group) => Some(CountRequest { filter, group }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {}", Self::COMMAND, self.filter);
|
||||
if let Some(group) = self.group.as_ref() {
|
||||
@@ -79,13 +84,15 @@ pub struct CountResponse {
|
||||
pub playtime: u64,
|
||||
}
|
||||
|
||||
impl CountResponse {
|
||||
pub fn new(songs: usize, playtime: u64) -> Self {
|
||||
CountResponse { songs, playtime }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for CountResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -17,21 +17,26 @@ pub struct FindRequest {
|
||||
window: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl FindRequest {
|
||||
pub fn new(filter: Filter, sort: Option<Sort>, window: Option<WindowRange>) -> Self {
|
||||
Self {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for FindRequest {
|
||||
const COMMAND: &'static str = "find";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Find(self.filter, self.sort, self.window)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Find(filter, sort, window) => Some(FindRequest {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {}", Self::COMMAND, self.filter);
|
||||
if let Some(sort) = &self.sort {
|
||||
@@ -106,13 +111,15 @@ impl CommandRequest for FindRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FindResponse(Vec<DbSongInfo>);
|
||||
|
||||
impl FindResponse {
|
||||
pub fn new(items: Vec<DbSongInfo>) -> Self {
|
||||
FindResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for FindResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
DbSelectionPrintResponse::parse(parts)?
|
||||
.into_iter()
|
||||
|
||||
@@ -17,27 +17,27 @@ pub struct FindAddRequest {
|
||||
position: Option<SongPosition>,
|
||||
}
|
||||
|
||||
impl FindAddRequest {
|
||||
pub fn new(
|
||||
filter: Filter,
|
||||
sort: Option<Sort>,
|
||||
window: Option<WindowRange>,
|
||||
position: Option<SongPosition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for FindAddRequest {
|
||||
const COMMAND: &'static str = "findadd";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(4);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::FindAdd(self.filter, self.sort, self.window, self.position)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::FindAdd(filter, sort, window, position) => Some(FindAddRequest {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
position,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {}", Self::COMMAND, self.filter);
|
||||
if let Some(sort) = &self.sort {
|
||||
|
||||
@@ -17,13 +17,15 @@ pub struct GetFingerprintResponse {
|
||||
pub chromaprint: String,
|
||||
}
|
||||
|
||||
impl GetFingerprintResponse {
|
||||
pub fn new(chromaprint: String) -> Self {
|
||||
Self { chromaprint }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for GetFingerprintResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -18,27 +18,27 @@ pub struct ListRequest {
|
||||
window: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl ListRequest {
|
||||
pub fn new(
|
||||
tagname: TagName,
|
||||
filter: Option<Filter>,
|
||||
groups: Vec<GroupType>,
|
||||
window: Option<WindowRange>,
|
||||
) -> Self {
|
||||
Self {
|
||||
tagname,
|
||||
filter,
|
||||
groups,
|
||||
window,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for ListRequest {
|
||||
const COMMAND: &'static str = "list";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = None;
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::List(self.tagname, self.filter, self.groups, self.window)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::List(tagname, filter, groups, window) => Some(ListRequest {
|
||||
tagname,
|
||||
filter,
|
||||
groups,
|
||||
window,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = match &self.filter {
|
||||
Some(f) => format!("{} {} {}", Self::COMMAND, self.tagname, f),
|
||||
@@ -138,13 +138,15 @@ impl CommandRequest for ListRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ListResponse(Vec<String>);
|
||||
|
||||
impl ListResponse {
|
||||
pub fn new(items: Vec<String>) -> Self {
|
||||
ListResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ListResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts_: Vec<_> = parts.into_vec()?;
|
||||
debug_assert!({
|
||||
|
||||
@@ -16,13 +16,15 @@ single_optional_item_command_request!(ListAll, "listall", Uri);
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ListAllResponse(Vec<DbSelectionPrintResponse>);
|
||||
|
||||
impl ListAllResponse {
|
||||
pub fn new(items: Vec<DbSelectionPrintResponse>) -> Self {
|
||||
ListAllResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ListAllResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let result = DbSelectionPrintResponse::parse(parts)?;
|
||||
|
||||
|
||||
@@ -16,13 +16,15 @@ single_optional_item_command_request!(ListAllInfo, "listallinfo", Uri);
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ListAllInfoResponse(Vec<DbSelectionPrintResponse>);
|
||||
|
||||
impl ListAllInfoResponse {
|
||||
pub fn new(items: Vec<DbSelectionPrintResponse>) -> Self {
|
||||
ListAllInfoResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ListAllInfoResponse {
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let result = DbSelectionPrintResponse::parse(parts)?;
|
||||
|
||||
|
||||
@@ -15,13 +15,15 @@ single_optional_item_command_request!(ListFiles, "listfiles", Uri);
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ListFilesResponse(Vec<DbDirectoryInfo>);
|
||||
|
||||
impl ListFilesResponse {
|
||||
pub fn new(items: Vec<DbDirectoryInfo>) -> Self {
|
||||
ListFilesResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ListFilesResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
DbSelectionPrintResponse::parse(parts)?
|
||||
.into_iter()
|
||||
|
||||
@@ -15,13 +15,15 @@ single_optional_item_command_request!(LsInfo, "lsinfo", Uri);
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct LsInfoResponse(Vec<DbSelectionPrintResponse>);
|
||||
|
||||
impl LsInfoResponse {
|
||||
pub fn new(items: Vec<DbSelectionPrintResponse>) -> Self {
|
||||
LsInfoResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for LsInfoResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let result = DbSelectionPrintResponse::parse(parts)?;
|
||||
|
||||
|
||||
@@ -15,13 +15,15 @@ single_item_command_request!(ReadComments, "readcomments", Uri);
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ReadCommentsResponse(HashMap<String, String>);
|
||||
|
||||
impl ReadCommentsResponse {
|
||||
pub fn new(comments: HashMap<String, String>) -> Self {
|
||||
ReadCommentsResponse(comments)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ReadCommentsResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -19,17 +19,22 @@ pub struct ReadPictureRequest {
|
||||
pub offset: Offset,
|
||||
}
|
||||
|
||||
impl ReadPictureRequest {
|
||||
pub fn new(uri: Uri, offset: Offset) -> Self {
|
||||
Self { uri, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for ReadPictureRequest {
|
||||
const COMMAND: &'static str = "readpicture";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::ReadPicture(self.uri, self.offset)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::ReadPicture(uri, offset) => Some(ReadPictureRequest { uri, offset }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.uri, self.offset)
|
||||
}
|
||||
@@ -67,17 +72,15 @@ pub struct ReadPictureResponse {
|
||||
pub mimetype: Option<String>,
|
||||
}
|
||||
|
||||
impl ReadPictureResponse {
|
||||
pub fn new(size: usize, binary: Vec<u8>, mimetype: Option<String>) -> Self {
|
||||
Self {
|
||||
size,
|
||||
binary,
|
||||
mimetype,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ReadPictureResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -19,13 +19,15 @@ pub struct RescanResponse {
|
||||
pub updating_db: usize,
|
||||
}
|
||||
|
||||
impl RescanResponse {
|
||||
pub fn new(updating_db: usize) -> Self {
|
||||
RescanResponse { updating_db }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for RescanResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -17,21 +17,26 @@ pub struct SearchRequest {
|
||||
window: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl SearchRequest {
|
||||
pub fn new(filter: Filter, sort: Option<Sort>, window: Option<WindowRange>) -> Self {
|
||||
Self {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SearchRequest {
|
||||
const COMMAND: &'static str = "search";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Search(self.filter, self.sort, self.window)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Search(filter, sort, window) => Some(SearchRequest {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {}", Self::COMMAND, self.filter);
|
||||
if let Some(sort) = &self.sort {
|
||||
@@ -106,13 +111,15 @@ impl CommandRequest for SearchRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SearchResponse(Vec<DbSongInfo>);
|
||||
|
||||
impl SearchResponse {
|
||||
pub fn new(items: Vec<DbSongInfo>) -> Self {
|
||||
SearchResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for SearchResponse {
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
DbSelectionPrintResponse::parse(parts)?
|
||||
.into_iter()
|
||||
|
||||
@@ -17,27 +17,27 @@ pub struct SearchAddRequest {
|
||||
position: Option<SongPosition>,
|
||||
}
|
||||
|
||||
impl SearchAddRequest {
|
||||
pub fn new(
|
||||
filter: Filter,
|
||||
sort: Option<Sort>,
|
||||
window: Option<WindowRange>,
|
||||
position: Option<SongPosition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SearchAddRequest {
|
||||
const COMMAND: &'static str = "searchadd";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(4);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::SearchAdd(self.filter, self.sort, self.window, self.position)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::SearchAdd(filter, sort, window, position) => Some(SearchAddRequest {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
position,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {}", Self::COMMAND, self.filter);
|
||||
if let Some(sort) = &self.sort {
|
||||
|
||||
@@ -18,29 +18,36 @@ pub struct SearchAddPlRequest {
|
||||
position: Option<SongPosition>,
|
||||
}
|
||||
|
||||
impl SearchAddPlRequest {
|
||||
pub fn new(
|
||||
playlist_name: PlaylistName,
|
||||
filter: Filter,
|
||||
sort: Option<Sort>,
|
||||
window: Option<WindowRange>,
|
||||
position: Option<SongPosition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
playlist_name,
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SearchAddPlRequest {
|
||||
const COMMAND: &'static str = "searchaddpl";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(5);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::SearchAddPl(
|
||||
self.playlist_name,
|
||||
self.filter,
|
||||
self.sort,
|
||||
self.window,
|
||||
self.position,
|
||||
)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::SearchAddPl(playlist_name, filter, sort, window, position) => {
|
||||
Some(SearchAddPlRequest {
|
||||
playlist_name,
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
position,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {} {}", Self::COMMAND, self.playlist_name, self.filter);
|
||||
if let Some(sort) = &self.sort {
|
||||
|
||||
@@ -18,17 +18,24 @@ pub struct SearchCountRequest {
|
||||
group: Option<GroupType>,
|
||||
}
|
||||
|
||||
impl SearchCountRequest {
|
||||
pub fn new(filter: Filter, group: Option<GroupType>) -> Self {
|
||||
Self { filter, group }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SearchCountRequest {
|
||||
const COMMAND: &'static str = "searchcount";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::SearchCount(self.filter, self.group)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::SearchCount(filter, group) => {
|
||||
Some(SearchCountRequest { filter, group })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {}", Self::COMMAND, self.filter);
|
||||
if let Some(group) = &self.group {
|
||||
@@ -78,13 +85,15 @@ pub struct SearchCountResponse {
|
||||
pub playtime: Seconds,
|
||||
}
|
||||
|
||||
impl SearchCountResponse {
|
||||
pub fn new(songs: usize, playtime: Seconds) -> Self {
|
||||
Self { songs, playtime }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for SearchCountResponse {
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -19,13 +19,15 @@ pub struct UpdateResponse {
|
||||
updating_db: usize,
|
||||
}
|
||||
|
||||
impl UpdateResponse {
|
||||
pub fn new(updating_db: usize) -> Self {
|
||||
UpdateResponse { updating_db }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for UpdateResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ use crate::{
|
||||
pub struct MixRampDelay;
|
||||
|
||||
single_item_command_request!(MixRampDelay, "mixrampdelay", Seconds);
|
||||
|
||||
empty_command_response!(MixRampDelay);
|
||||
|
||||
impl Command for MixRampDelay {
|
||||
|
||||
@@ -7,17 +7,22 @@ pub struct Random;
|
||||
|
||||
pub struct RandomRequest(bool);
|
||||
|
||||
impl RandomRequest {
|
||||
pub fn new(state: bool) -> Self {
|
||||
Self(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for RandomRequest {
|
||||
const COMMAND: &'static str = "random";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(1);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Random(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Random(state) => Some(RandomRequest(state)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let state = if self.0 { "1" } else { "0" };
|
||||
format!("{} {}\n", Self::COMMAND, state)
|
||||
|
||||
@@ -7,17 +7,22 @@ pub struct Repeat;
|
||||
|
||||
pub struct RepeatRequest(bool);
|
||||
|
||||
impl RepeatRequest {
|
||||
pub fn new(state: bool) -> Self {
|
||||
RepeatRequest(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for RepeatRequest {
|
||||
const COMMAND: &'static str = "repeat";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(1);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Repeat(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Repeat(state) => Some(RepeatRequest(state)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let state = if self.0 { "1" } else { "0" };
|
||||
format!("{} {}\n", Self::COMMAND, state)
|
||||
|
||||
@@ -17,13 +17,15 @@ pub struct ReplayGainStatusResponse {
|
||||
pub replay_gain_mode: ReplayGainModeMode,
|
||||
}
|
||||
|
||||
impl ReplayGainStatusResponse {
|
||||
pub fn new(replay_gain_mode: ReplayGainModeMode) -> Self {
|
||||
Self { replay_gain_mode }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ReplayGainStatusResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
let replay_gain_mode = get_property!(parts, "replay_gain_mode", Text);
|
||||
|
||||
@@ -23,23 +23,15 @@ pub struct CurrentSongResponse {
|
||||
song_info: DbSongInfo,
|
||||
}
|
||||
|
||||
impl CurrentSongResponse {
|
||||
pub fn new(
|
||||
position: SongPosition,
|
||||
id: SongId,
|
||||
priority: Option<Priority>,
|
||||
song_info: DbSongInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
position,
|
||||
id,
|
||||
priority,
|
||||
song_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for CurrentSongResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let mut parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -10,17 +10,22 @@ pub struct Idle;
|
||||
|
||||
pub struct IdleRequest(Option<Vec<SubSystem>>);
|
||||
|
||||
impl IdleRequest {
|
||||
pub fn new(subsystems: Option<Vec<SubSystem>>) -> Self {
|
||||
IdleRequest(subsystems)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for IdleRequest {
|
||||
const COMMAND: &'static str = "idle";
|
||||
const MIN_ARGS: u32 = 0;
|
||||
const MAX_ARGS: Option<u32> = None;
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Idle(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Idle(subsystems) => Some(IdleRequest(subsystems)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match &self.0 {
|
||||
Some(subsystems) => {
|
||||
|
||||
@@ -24,29 +24,15 @@ pub struct StatsResponse {
|
||||
pub db_update: Option<u64>,
|
||||
}
|
||||
|
||||
impl StatsResponse {
|
||||
pub fn new(
|
||||
uptime: u64,
|
||||
playtime: u64,
|
||||
artists: Option<u64>,
|
||||
albums: Option<u64>,
|
||||
songs: Option<u64>,
|
||||
db_playtime: Option<u64>,
|
||||
db_update: Option<u64>,
|
||||
) -> Self {
|
||||
Self {
|
||||
uptime,
|
||||
playtime,
|
||||
artists,
|
||||
albums,
|
||||
songs,
|
||||
db_playtime,
|
||||
db_update,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for StatsResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -65,63 +65,15 @@ pub struct StatusResponse {
|
||||
pub last_loaded_playlist: Option<String>,
|
||||
}
|
||||
|
||||
impl StatusResponse {
|
||||
pub fn new(
|
||||
partition: String,
|
||||
volume: Option<u8>,
|
||||
repeat: bool,
|
||||
random: bool,
|
||||
single: BoolOrOneshot,
|
||||
consume: BoolOrOneshot,
|
||||
playlist: u32,
|
||||
playlist_length: u64,
|
||||
state: StatusResponseState,
|
||||
song: Option<SongPosition>,
|
||||
song_id: Option<SongId>,
|
||||
next_song: Option<SongPosition>,
|
||||
next_song_id: Option<SongId>,
|
||||
time: Option<(u64, u64)>,
|
||||
elapsed: Option<f64>,
|
||||
duration: Option<f64>,
|
||||
bitrate: Option<u32>,
|
||||
xfade: Option<u32>,
|
||||
mixrampdb: Option<f64>,
|
||||
mixrampdelay: Option<f64>,
|
||||
audio: Option<Audio>,
|
||||
updating_db: Option<u64>,
|
||||
error: Option<String>,
|
||||
last_loaded_playlist: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
partition,
|
||||
volume,
|
||||
repeat,
|
||||
random,
|
||||
single,
|
||||
consume,
|
||||
playlist,
|
||||
playlist_length,
|
||||
state,
|
||||
song,
|
||||
song_id,
|
||||
next_song,
|
||||
next_song_id,
|
||||
time,
|
||||
elapsed,
|
||||
duration,
|
||||
bitrate,
|
||||
xfade,
|
||||
mixrampdb,
|
||||
mixrampdelay,
|
||||
audio,
|
||||
updating_db,
|
||||
error,
|
||||
last_loaded_playlist,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for StatusResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
let partition = get_property!(parts, "partition", Text).to_string();
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct AddRequest {
|
||||
position: Option<SongPosition>,
|
||||
}
|
||||
|
||||
impl AddRequest {
|
||||
pub fn new(uri: Uri, position: Option<SongPosition>) -> Self {
|
||||
Self { uri, position }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for AddRequest {
|
||||
const COMMAND: &'static str = "add";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Add(self.uri, self.position)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Add(uri, position) => Some(AddRequest { uri, position }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match self.position {
|
||||
Some(position) => format!("{} {} {}\n", Self::COMMAND, self.uri, position),
|
||||
|
||||
+19
-12
@@ -15,17 +15,22 @@ pub struct AddIdRequest {
|
||||
pub position: Option<SongPosition>,
|
||||
}
|
||||
|
||||
impl AddIdRequest {
|
||||
pub fn new(uri: Uri, position: Option<SongPosition>) -> Self {
|
||||
Self { uri, position }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for AddIdRequest {
|
||||
const COMMAND: &'static str = "addid";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::AddId(self.uri, self.position)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::AddId(uri, position) => Some(AddIdRequest { uri, position }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match self.position {
|
||||
Some(pos) => format!("{} {} {}\n", Self::COMMAND, self.uri, pos),
|
||||
@@ -64,13 +69,15 @@ pub struct AddIdResponse {
|
||||
pub id: SongId,
|
||||
}
|
||||
|
||||
impl AddIdResponse {
|
||||
pub fn new(id: SongId) -> Self {
|
||||
Self { id }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for AddIdResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into();
|
||||
let mut iter = parts.into_iter();
|
||||
|
||||
@@ -15,21 +15,26 @@ pub struct AddTagIdRequest {
|
||||
pub tag_value: TagValue,
|
||||
}
|
||||
|
||||
impl AddTagIdRequest {
|
||||
pub fn new(songid: SongId, tag_name: TagName, tag_value: TagValue) -> Self {
|
||||
Self {
|
||||
songid,
|
||||
tag_name,
|
||||
tag_value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for AddTagIdRequest {
|
||||
const COMMAND: &'static str = "addtagid";
|
||||
const MIN_ARGS: u32 = 3;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::AddTagId(self.songid, self.tag_name, self.tag_value)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::AddTagId(songid, tag_name, tag_value) => Some(AddTagIdRequest {
|
||||
songid,
|
||||
tag_name,
|
||||
tag_value,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {} {} {}\n",
|
||||
|
||||
@@ -14,17 +14,24 @@ pub struct ClearTagIdRequest {
|
||||
pub tag_name: TagName,
|
||||
}
|
||||
|
||||
impl ClearTagIdRequest {
|
||||
pub fn new(songid: SongId, tag_name: TagName) -> Self {
|
||||
Self { songid, tag_name }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for ClearTagIdRequest {
|
||||
const COMMAND: &'static str = "cleartagid";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::ClearTagId(self.songid, self.tag_name)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::ClearTagId(songid, tag_name) => {
|
||||
Some(ClearTagIdRequest { songid, tag_name })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.songid, self.tag_name)
|
||||
}
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct MoveRequest {
|
||||
pub to: AbsouluteRelativeSongPosition,
|
||||
}
|
||||
|
||||
impl MoveRequest {
|
||||
pub fn new(from_or_range: OneOrRange, to: AbsouluteRelativeSongPosition) -> Self {
|
||||
Self { from_or_range, to }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for MoveRequest {
|
||||
const COMMAND: &'static str = "move";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Move(self.from_or_range, self.to)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Move(from_or_range, to) => Some(MoveRequest { from_or_range, to }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.from_or_range, self.to)
|
||||
}
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct MoveIdRequest {
|
||||
pub to: AbsouluteRelativeSongPosition,
|
||||
}
|
||||
|
||||
impl MoveIdRequest {
|
||||
pub fn new(id: SongId, to: AbsouluteRelativeSongPosition) -> Self {
|
||||
Self { id, to }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for MoveIdRequest {
|
||||
const COMMAND: &'static str = "moveid";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::MoveId(self.id, self.to)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::MoveId(id, to) => Some(MoveIdRequest { id, to }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.id, self.to)
|
||||
}
|
||||
|
||||
@@ -13,14 +13,16 @@ empty_command_request!(Playlist, "playlist");
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlaylistResponse(Vec<Uri>);
|
||||
|
||||
impl PlaylistResponse {
|
||||
pub fn new(items: Vec<Uri>) -> Self {
|
||||
PlaylistResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for PlaylistResponse {
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
Response,
|
||||
commands::{Command, CommandRequest, CommandResponse, RequestParserError, ResponseParserError},
|
||||
filter::Filter,
|
||||
request_tokenizer::RequestTokenizer,
|
||||
@@ -17,21 +18,26 @@ pub struct PlaylistFindRequest {
|
||||
window: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl PlaylistFindRequest {
|
||||
pub fn new(filter: Filter, sort: Option<Sort>, window: Option<WindowRange>) -> Self {
|
||||
Self {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for PlaylistFindRequest {
|
||||
const COMMAND: &'static str = "playlistfind";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::PlaylistFind(self.filter, self.sort, self.window)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::PlaylistFind(filter, sort, window) => Some(PlaylistFindRequest {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {}", Self::COMMAND, self.filter);
|
||||
if let Some(sort) = &self.sort {
|
||||
@@ -106,12 +112,6 @@ impl CommandRequest for PlaylistFindRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlaylistFindResponse(Vec<PlaylistFindResponseEntry>);
|
||||
|
||||
impl PlaylistFindResponse {
|
||||
pub fn new(items: Vec<PlaylistFindResponseEntry>) -> Self {
|
||||
PlaylistFindResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlaylistFindResponseEntry {
|
||||
pub position: SongPosition,
|
||||
@@ -120,23 +120,15 @@ pub struct PlaylistFindResponseEntry {
|
||||
pub song_info: DbSongInfo,
|
||||
}
|
||||
|
||||
impl PlaylistFindResponseEntry {
|
||||
pub fn new(
|
||||
position: SongPosition,
|
||||
id: SongId,
|
||||
priority: Option<Priority>,
|
||||
song_info: DbSongInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
position,
|
||||
id,
|
||||
priority,
|
||||
song_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for PlaylistFindResponse {
|
||||
fn into_response_enum(self) -> Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -13,12 +13,6 @@ single_item_command_request!(PlaylistId, "playlistid", SongId);
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlaylistIdResponse(Vec<PlaylistIdResponseEntry>);
|
||||
|
||||
impl PlaylistIdResponse {
|
||||
pub fn new(items: Vec<PlaylistIdResponseEntry>) -> Self {
|
||||
PlaylistIdResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlaylistIdResponseEntry {
|
||||
pub position: SongPosition,
|
||||
@@ -27,24 +21,16 @@ pub struct PlaylistIdResponseEntry {
|
||||
pub song_info: DbSongInfo,
|
||||
}
|
||||
|
||||
impl PlaylistIdResponseEntry {
|
||||
pub fn new(
|
||||
position: SongPosition,
|
||||
id: SongId,
|
||||
priority: Option<Priority>,
|
||||
song_info: DbSongInfo,
|
||||
) -> Self {
|
||||
PlaylistIdResponseEntry {
|
||||
position,
|
||||
id,
|
||||
priority,
|
||||
song_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for PlaylistIdResponse {
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,6 @@ single_optional_item_command_request!(PlaylistInfo, "playlistinfo", OneOrRange);
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlaylistInfoResponse(Vec<PlaylistInfoResponseEntry>);
|
||||
|
||||
impl PlaylistInfoResponse {
|
||||
pub fn new(items: Vec<PlaylistInfoResponseEntry>) -> Self {
|
||||
PlaylistInfoResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlaylistInfoResponseEntry {
|
||||
pub position: SongPosition,
|
||||
@@ -29,23 +23,15 @@ pub struct PlaylistInfoResponseEntry {
|
||||
pub song_info: DbSongInfo,
|
||||
}
|
||||
|
||||
impl PlaylistInfoResponseEntry {
|
||||
pub fn new(
|
||||
position: SongPosition,
|
||||
id: SongId,
|
||||
priority: Option<Priority>,
|
||||
song_info: DbSongInfo,
|
||||
) -> Self {
|
||||
PlaylistInfoResponseEntry {
|
||||
position,
|
||||
id,
|
||||
priority,
|
||||
song_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for PlaylistInfoResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -17,21 +17,26 @@ pub struct PlaylistSearchRequest {
|
||||
window: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl PlaylistSearchRequest {
|
||||
pub fn new(filter: Filter, sort: Option<Sort>, window: Option<WindowRange>) -> Self {
|
||||
Self {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for PlaylistSearchRequest {
|
||||
const COMMAND: &'static str = "playlistsearch";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::PlaylistSearch(self.filter, self.sort, self.window)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::PlaylistSearch(filter, sort, window) => Some(PlaylistSearchRequest {
|
||||
filter,
|
||||
sort,
|
||||
window,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {}", Self::COMMAND, self.filter);
|
||||
if let Some(sort) = &self.sort {
|
||||
@@ -106,12 +111,6 @@ impl CommandRequest for PlaylistSearchRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlaylistSearchResponse(Vec<PlaylistSearchResponseEntry>);
|
||||
|
||||
impl PlaylistSearchResponse {
|
||||
pub fn new(items: Vec<PlaylistSearchResponseEntry>) -> Self {
|
||||
PlaylistSearchResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlaylistSearchResponseEntry {
|
||||
pub position: SongPosition,
|
||||
@@ -120,24 +119,16 @@ pub struct PlaylistSearchResponseEntry {
|
||||
pub song_info: DbSongInfo,
|
||||
}
|
||||
|
||||
impl PlaylistSearchResponseEntry {
|
||||
pub fn new(
|
||||
position: SongPosition,
|
||||
id: SongId,
|
||||
priority: Option<Priority>,
|
||||
song_info: DbSongInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
position,
|
||||
id,
|
||||
priority,
|
||||
song_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for PlaylistSearchResponse {
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,24 @@ pub struct PlChangesRequest {
|
||||
pub window: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl PlChangesRequest {
|
||||
pub fn new(version: PlaylistVersion, window: Option<WindowRange>) -> Self {
|
||||
Self { version, window }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for PlChangesRequest {
|
||||
const COMMAND: &'static str = "plchanges";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::PlChanges(self.version, self.window)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::PlChanges(version, window) => {
|
||||
Some(PlChangesRequest { version, window })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match self.window.as_ref() {
|
||||
Some(window) => format!("{} {} {}\n", Self::COMMAND, self.version, window),
|
||||
@@ -64,12 +71,6 @@ impl CommandRequest for PlChangesRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlChangesResponse(Vec<PlChangesResponseEntry>);
|
||||
|
||||
impl PlChangesResponse {
|
||||
pub fn new(items: Vec<PlChangesResponseEntry>) -> Self {
|
||||
PlChangesResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlChangesResponseEntry {
|
||||
pub position: SongPosition,
|
||||
@@ -78,24 +79,16 @@ pub struct PlChangesResponseEntry {
|
||||
pub song_info: DbSongInfo,
|
||||
}
|
||||
|
||||
impl PlChangesResponseEntry {
|
||||
pub fn new(
|
||||
position: SongPosition,
|
||||
id: SongId,
|
||||
priority: Option<Priority>,
|
||||
song_info: DbSongInfo,
|
||||
) -> Self {
|
||||
PlChangesResponseEntry {
|
||||
position,
|
||||
id,
|
||||
priority,
|
||||
song_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for PlChangesResponse {
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,24 @@ pub struct PlChangesPosIdRequest {
|
||||
pub window: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl PlChangesPosIdRequest {
|
||||
pub fn new(version: PlaylistVersion, window: Option<WindowRange>) -> Self {
|
||||
Self { version, window }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for PlChangesPosIdRequest {
|
||||
const COMMAND: &'static str = "plchangesposid";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::PlChangesPosId(self.version, self.window)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::PlChangesPosId(version, window) => {
|
||||
Some(PlChangesPosIdRequest { version, window })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match self.window.as_ref() {
|
||||
Some(window) => format!("{} {} {}\n", Self::COMMAND, self.version, window),
|
||||
@@ -64,12 +71,6 @@ impl CommandRequest for PlChangesPosIdRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlChangesPosIdResponse(Vec<PlChangesPosIdResponseEntry>);
|
||||
|
||||
impl PlChangesPosIdResponse {
|
||||
pub fn new(items: Vec<PlChangesPosIdResponseEntry>) -> Self {
|
||||
PlChangesPosIdResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PlChangesPosIdResponseEntry {
|
||||
// 'cpos'
|
||||
@@ -77,14 +78,16 @@ pub struct PlChangesPosIdResponseEntry {
|
||||
pub id: SongId,
|
||||
}
|
||||
|
||||
impl PlChangesPosIdResponseEntry {
|
||||
pub fn new(position: SongPosition, id: SongId) -> Self {
|
||||
PlChangesPosIdResponseEntry { position, id }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for PlChangesPosIdResponse {
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct PrioRequest {
|
||||
pub window: WindowRange,
|
||||
}
|
||||
|
||||
impl PrioRequest {
|
||||
pub fn new(prio: Priority, window: WindowRange) -> Self {
|
||||
Self { prio, window }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for PrioRequest {
|
||||
const COMMAND: &'static str = "prio";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Prio(self.prio, self.window)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Prio(prio, window) => Some(PrioRequest { prio, window }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.prio, self.window)
|
||||
}
|
||||
|
||||
@@ -14,18 +14,23 @@ pub struct PrioIdRequest {
|
||||
pub songids: Vec<SongId>,
|
||||
}
|
||||
|
||||
impl PrioIdRequest {
|
||||
pub fn new(prio: Priority, songids: Vec<SongId>) -> Self {
|
||||
Self { prio, songids }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for PrioIdRequest {
|
||||
const COMMAND: &'static str = "prioid";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
// TODO: should this be 2?
|
||||
const MAX_ARGS: Option<u32> = None;
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::PrioId(self.prio, self.songids)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::PrioId(prio, songids) => Some(PrioIdRequest { prio, songids }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let songids = self
|
||||
.songids
|
||||
|
||||
@@ -14,20 +14,25 @@ pub struct RangeIdRequest {
|
||||
time_interval: TimeInterval,
|
||||
}
|
||||
|
||||
impl RangeIdRequest {
|
||||
pub fn new(songid: SongId, time_interval: TimeInterval) -> Self {
|
||||
Self {
|
||||
songid,
|
||||
time_interval,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for RangeIdRequest {
|
||||
const COMMAND: &'static str = "rangeid";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::RangeId(self.songid, self.time_interval)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::RangeId(songid, time_interval) => Some(RangeIdRequest {
|
||||
songid,
|
||||
time_interval,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.songid, self.time_interval)
|
||||
}
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct SwapRequest {
|
||||
pub songpos2: SongPosition,
|
||||
}
|
||||
|
||||
impl SwapRequest {
|
||||
pub fn new(songpos1: SongPosition, songpos2: SongPosition) -> Self {
|
||||
Self { songpos1, songpos2 }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SwapRequest {
|
||||
const COMMAND: &'static str = "swap";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Swap(self.songpos1, self.songpos2)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Swap(songpos1, songpos2) => Some(SwapRequest { songpos1, songpos2 }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.songpos1, self.songpos2)
|
||||
}
|
||||
|
||||
@@ -14,17 +14,22 @@ pub struct SwapIdRequest {
|
||||
pub songid2: SongId,
|
||||
}
|
||||
|
||||
impl SwapIdRequest {
|
||||
pub fn new(songid1: SongId, songid2: SongId) -> Self {
|
||||
Self { songid1, songid2 }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SwapIdRequest {
|
||||
const COMMAND: &'static str = "swapid";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::SwapId(self.songid1, self.songid2)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::SwapId(songid1, songid2) => Some(SwapIdRequest { songid1, songid2 }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.songid1, self.songid2)
|
||||
}
|
||||
|
||||
@@ -18,17 +18,15 @@ pub struct ConfigResponse {
|
||||
pub pcre: bool,
|
||||
}
|
||||
|
||||
impl ConfigResponse {
|
||||
pub fn new(music_directory: String, playlist_directory: String, pcre: bool) -> Self {
|
||||
ConfigResponse {
|
||||
music_directory,
|
||||
playlist_directory,
|
||||
pcre,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ConfigResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -16,26 +16,18 @@ pub struct Decoder {
|
||||
pub mime_types: Vec<String>,
|
||||
}
|
||||
|
||||
impl Decoder {
|
||||
pub fn new(plugin: String, suffixes: Vec<String>, mime_types: Vec<String>) -> Self {
|
||||
Decoder {
|
||||
plugin,
|
||||
suffixes,
|
||||
mime_types,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct DecodersResponse(Vec<Decoder>);
|
||||
|
||||
impl DecodersResponse {
|
||||
pub fn new(items: Vec<Decoder>) -> Self {
|
||||
DecodersResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for DecodersResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let mut result = Vec::new();
|
||||
let mut current_decoder: Option<Decoder> = None;
|
||||
|
||||
@@ -16,22 +16,27 @@ pub struct StickerDecRequest {
|
||||
pub value: usize,
|
||||
}
|
||||
|
||||
impl StickerDecRequest {
|
||||
pub fn new(sticker_type: StickerType, uri: Uri, name: String, value: usize) -> Self {
|
||||
Self {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for StickerDecRequest {
|
||||
const COMMAND: &'static str = "sticker dec";
|
||||
const MIN_ARGS: u32 = 4;
|
||||
const MAX_ARGS: Option<u32> = Some(4);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::StickerDec(self.sticker_type, self.uri, self.name, self.value)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::StickerDec(sticker_type, uri, name, value) => Some(StickerDecRequest {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
value,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {} {} {} {}\n",
|
||||
|
||||
@@ -15,21 +15,26 @@ pub struct StickerDeleteRequest {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl StickerDeleteRequest {
|
||||
pub fn new(sticker_type: StickerType, uri: Uri, name: String) -> Self {
|
||||
Self {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for StickerDeleteRequest {
|
||||
const COMMAND: &'static str = "sticker delete";
|
||||
const MIN_ARGS: u32 = 3;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::StickerDelete(self.sticker_type, self.uri, self.name)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::StickerDelete(sticker_type, uri, name) => Some(StickerDeleteRequest {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {} {} {}\n",
|
||||
|
||||
@@ -18,29 +18,36 @@ pub struct StickerFindRequest {
|
||||
pub window: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl StickerFindRequest {
|
||||
pub fn new(
|
||||
sticker_type: StickerType,
|
||||
uri: Uri,
|
||||
name: String,
|
||||
sort: Option<Sort>,
|
||||
window: Option<WindowRange>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
sort,
|
||||
window,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for StickerFindRequest {
|
||||
const COMMAND: &'static str = "sticker find";
|
||||
const MIN_ARGS: u32 = 3;
|
||||
const MAX_ARGS: Option<u32> = Some(5);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::StickerFind(
|
||||
self.sticker_type,
|
||||
self.uri,
|
||||
self.name,
|
||||
self.sort,
|
||||
self.window,
|
||||
)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::StickerFind(sticker_type, uri, name, sort, window) => {
|
||||
Some(StickerFindRequest {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
sort,
|
||||
window,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!(
|
||||
"{} {} {} {}",
|
||||
@@ -144,12 +151,6 @@ impl CommandRequest for StickerFindRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StickerFindResponse(Vec<StickerFindResponseEntry>);
|
||||
|
||||
impl StickerFindResponse {
|
||||
pub fn new(items: Vec<StickerFindResponseEntry>) -> Self {
|
||||
StickerFindResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StickerFindResponseEntry {
|
||||
pub uri: String,
|
||||
@@ -157,13 +158,15 @@ pub struct StickerFindResponseEntry {
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl StickerFindResponseEntry {
|
||||
pub fn new(uri: String, name: String, value: String) -> Self {
|
||||
Self { uri, name, value }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for StickerFindResponse {
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
let mut stickers = Vec::with_capacity(parts.len() / 2);
|
||||
|
||||
@@ -15,21 +15,26 @@ pub struct StickerGetRequest {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl StickerGetRequest {
|
||||
pub fn new(sticker_type: StickerType, uri: Uri, name: String) -> Self {
|
||||
Self {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for StickerGetRequest {
|
||||
const COMMAND: &'static str = "sticker get";
|
||||
const MIN_ARGS: u32 = 3;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::StickerGet(self.sticker_type, self.uri, self.name)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::StickerGet(sticker_type, uri, name) => Some(StickerGetRequest {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {} {} {}\n",
|
||||
|
||||
@@ -16,22 +16,27 @@ pub struct StickerIncRequest {
|
||||
pub value: usize,
|
||||
}
|
||||
|
||||
impl StickerIncRequest {
|
||||
pub fn new(sticker_type: StickerType, uri: Uri, name: String, value: usize) -> Self {
|
||||
Self {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for StickerIncRequest {
|
||||
const COMMAND: &'static str = "sticker inc";
|
||||
const MIN_ARGS: u32 = 4;
|
||||
const MAX_ARGS: Option<u32> = Some(4);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::StickerInc(self.sticker_type, self.uri, self.name, self.value)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::StickerInc(sticker_type, uri, name, value) => Some(StickerIncRequest {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
value,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {} {} {} {}\n",
|
||||
|
||||
@@ -17,17 +17,24 @@ pub struct StickerListRequest {
|
||||
pub uri: Uri,
|
||||
}
|
||||
|
||||
impl StickerListRequest {
|
||||
pub fn new(sticker_type: StickerType, uri: Uri) -> Self {
|
||||
Self { sticker_type, uri }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for StickerListRequest {
|
||||
const COMMAND: &'static str = "sticker list";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::StickerList(self.sticker_type, self.uri)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::StickerList(sticker_type, uri) => {
|
||||
Some(StickerListRequest { sticker_type, uri })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.sticker_type, self.uri)
|
||||
}
|
||||
@@ -61,13 +68,15 @@ impl CommandRequest for StickerListRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StickerListResponse(HashMap<String, String>);
|
||||
|
||||
impl StickerListResponse {
|
||||
pub fn new(map: HashMap<String, String>) -> Self {
|
||||
StickerListResponse(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for StickerListResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
if let Some((k, _)) = parts.iter().find(|(k, _)| *k != "sticker") {
|
||||
|
||||
@@ -16,22 +16,27 @@ pub struct StickerSetRequest {
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl StickerSetRequest {
|
||||
pub fn new(sticker_type: StickerType, uri: Uri, name: String, value: String) -> Self {
|
||||
Self {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for StickerSetRequest {
|
||||
const COMMAND: &'static str = "sticker set";
|
||||
const MIN_ARGS: u32 = 4;
|
||||
const MAX_ARGS: Option<u32> = Some(4);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::StickerSet(self.sticker_type, self.uri, self.name, self.value)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::StickerSet(sticker_type, uri, name, value) => Some(StickerSetRequest {
|
||||
sticker_type,
|
||||
uri,
|
||||
name,
|
||||
value,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {} {} {} {}\n",
|
||||
|
||||
@@ -14,17 +14,24 @@ pub struct StickerNamesTypes;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StickerNamesTypesRequest(Option<StickerType>);
|
||||
|
||||
impl StickerNamesTypesRequest {
|
||||
pub fn new(sticker_type: Option<StickerType>) -> Self {
|
||||
Self(sticker_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for StickerNamesTypesRequest {
|
||||
const COMMAND: &'static str = "stickernamestypes";
|
||||
const MIN_ARGS: u32 = 0;
|
||||
const MAX_ARGS: Option<u32> = Some(1);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::StickerNamesTypes(self.0)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::StickerNamesTypes(sticker_type) => {
|
||||
Some(StickerNamesTypesRequest(sticker_type))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
if let Some(sticker_type) = &self.0 {
|
||||
format!("{} {}\n", Self::COMMAND, sticker_type)
|
||||
@@ -52,13 +59,15 @@ impl CommandRequest for StickerNamesTypesRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StickerNamesTypesResponse(HashMap<String, Vec<String>>);
|
||||
|
||||
impl StickerNamesTypesResponse {
|
||||
pub fn new(map: HashMap<String, Vec<String>>) -> Self {
|
||||
StickerNamesTypesResponse(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for StickerNamesTypesResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
debug_assert!(parts.len() % 2 == 0);
|
||||
|
||||
@@ -20,17 +20,22 @@ pub struct ListPlaylistRequest {
|
||||
range: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl ListPlaylistRequest {
|
||||
pub fn new(name: PlaylistName, range: Option<WindowRange>) -> Self {
|
||||
Self { name, range }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for ListPlaylistRequest {
|
||||
const COMMAND: &'static str = "listplaylist";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::ListPlaylist(self.name, self.range)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::ListPlaylist(name, range) => Some(ListPlaylistRequest { name, range }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
if let Some(range) = &self.range {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.name, range)
|
||||
|
||||
@@ -15,17 +15,24 @@ pub struct ListPlaylistInfoRequest {
|
||||
range: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl ListPlaylistInfoRequest {
|
||||
pub fn new(name: PlaylistName, range: Option<WindowRange>) -> Self {
|
||||
Self { name, range }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for ListPlaylistInfoRequest {
|
||||
const COMMAND: &'static str = "listplaylistinfo";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::ListPlaylistInfo(self.name, self.range)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::ListPlaylistInfo(name, range) => {
|
||||
Some(ListPlaylistInfoRequest { name, range })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match self.range.as_ref() {
|
||||
Some(range) => format!("{} {} {}\n", Self::COMMAND, self.name, range),
|
||||
@@ -56,14 +63,16 @@ impl CommandRequest for ListPlaylistInfoRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ListPlaylistInfoResponse(Vec<DbSongInfo>);
|
||||
|
||||
impl ListPlaylistInfoResponse {
|
||||
pub fn new(items: Vec<DbSongInfo>) -> Self {
|
||||
ListPlaylistInfoResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ListPlaylistInfoResponse {
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,6 @@ empty_command_request!(ListPlaylists, "listplaylists");
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ListPlaylistsResponse(Vec<ListPlaylistsResponseEntry>);
|
||||
|
||||
impl ListPlaylistsResponse {
|
||||
pub fn new(items: Vec<ListPlaylistsResponseEntry>) -> Self {
|
||||
ListPlaylistsResponse(items)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ListPlaylistsResponseEntry {
|
||||
pub playlist: PlaylistName,
|
||||
@@ -26,16 +20,15 @@ pub struct ListPlaylistsResponseEntry {
|
||||
pub last_modified: Option<String>,
|
||||
}
|
||||
|
||||
impl ListPlaylistsResponseEntry {
|
||||
pub fn new(playlist: PlaylistName, last_modified: Option<String>) -> Self {
|
||||
ListPlaylistsResponseEntry {
|
||||
playlist,
|
||||
last_modified,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for ListPlaylistsResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let mut parts = parts.into_lazy_vec().into_iter().peekable();
|
||||
// TODO: count instances of 'playlist' to preallocate
|
||||
|
||||
@@ -15,25 +15,26 @@ pub struct LoadRequest {
|
||||
position: Option<SongPosition>,
|
||||
}
|
||||
|
||||
impl LoadRequest {
|
||||
pub fn new(
|
||||
name: PlaylistName,
|
||||
range: Option<WindowRange>,
|
||||
position: Option<SongPosition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
range,
|
||||
position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for LoadRequest {
|
||||
const COMMAND: &'static str = "load";
|
||||
const MIN_ARGS: u32 = 1;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Load(self.name, self.range, self.position)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Load(name, range, position) => Some(LoadRequest {
|
||||
name,
|
||||
range,
|
||||
position,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match (self.range.as_ref(), self.position.as_ref()) {
|
||||
(Some(range), Some(pos)) => {
|
||||
|
||||
@@ -15,21 +15,26 @@ pub struct PlaylistAddRequest {
|
||||
pub position: Option<SongPosition>,
|
||||
}
|
||||
|
||||
impl PlaylistAddRequest {
|
||||
pub fn new(playlist_name: PlaylistName, uri: Uri, position: Option<SongPosition>) -> Self {
|
||||
Self {
|
||||
playlist_name,
|
||||
uri,
|
||||
position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for PlaylistAddRequest {
|
||||
const COMMAND: &'static str = "playlistadd";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::PlaylistAdd(self.playlist_name, self.uri, self.position)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::PlaylistAdd(playlist_name, uri, position) => Some(PlaylistAddRequest {
|
||||
playlist_name,
|
||||
uri,
|
||||
position,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match self.position {
|
||||
Some(pos) => {
|
||||
|
||||
@@ -14,20 +14,27 @@ pub struct PlaylistDeleteRequest {
|
||||
pub position: OneOrRange,
|
||||
}
|
||||
|
||||
impl PlaylistDeleteRequest {
|
||||
pub fn new(playlist_name: PlaylistName, position: OneOrRange) -> Self {
|
||||
Self {
|
||||
playlist_name,
|
||||
position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for PlaylistDeleteRequest {
|
||||
const COMMAND: &'static str = "playlistdelete";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::PlaylistDelete(self.playlist_name, self.position)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::PlaylistDelete(playlist_name, position) => {
|
||||
Some(PlaylistDeleteRequest {
|
||||
playlist_name,
|
||||
position,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{} {} {}\n",
|
||||
|
||||
@@ -18,13 +18,15 @@ pub struct PlaylistLengthResponse {
|
||||
pub playtime: u64,
|
||||
}
|
||||
|
||||
impl PlaylistLengthResponse {
|
||||
pub fn new(songs: u64, playtime: u64) -> Self {
|
||||
PlaylistLengthResponse { songs, playtime }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandResponse for PlaylistLengthResponse {
|
||||
fn into_response_enum(self) -> crate::Response {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn from_response_enum(response: crate::Response) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
|
||||
@@ -15,21 +15,26 @@ pub struct PlaylistMoveRequest {
|
||||
pub to: SongPosition,
|
||||
}
|
||||
|
||||
impl PlaylistMoveRequest {
|
||||
pub fn new(playlist_name: PlaylistName, from: Option<OneOrRange>, to: SongPosition) -> Self {
|
||||
Self {
|
||||
playlist_name,
|
||||
from,
|
||||
to,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for PlaylistMoveRequest {
|
||||
const COMMAND: &'static str = "playlistmove";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::PlaylistMove(self.playlist_name, self.from, self.to)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::PlaylistMove(playlist_name, from, to) => Some(PlaylistMoveRequest {
|
||||
playlist_name,
|
||||
from,
|
||||
to,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match self.from.as_ref() {
|
||||
Some(from) => {
|
||||
|
||||
@@ -13,17 +13,24 @@ pub struct RenameRequest {
|
||||
pub new_name: String,
|
||||
}
|
||||
|
||||
impl RenameRequest {
|
||||
pub fn new(old_name: String, new_name: String) -> Self {
|
||||
Self { old_name, new_name }
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for RenameRequest {
|
||||
const COMMAND: &'static str = "rename";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(2);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Rename(self.old_name, self.new_name)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Rename(old_name, new_name) => {
|
||||
Some(RenameRequest { old_name, new_name })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
format!("{} {} {}\n", Self::COMMAND, self.old_name, self.new_name)
|
||||
}
|
||||
|
||||
@@ -14,20 +14,25 @@ pub struct SaveRequest {
|
||||
pub mode: Option<SaveMode>,
|
||||
}
|
||||
|
||||
impl SaveRequest {
|
||||
pub fn new(playlist_name: PlaylistName, mode: Option<SaveMode>) -> Self {
|
||||
Self {
|
||||
playlist_name,
|
||||
mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SaveRequest {
|
||||
const COMMAND: &'static str = "save";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::Save(self.playlist_name, self.mode)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::Save(playlist_name, mode) => Some(SaveRequest {
|
||||
playlist_name,
|
||||
mode,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
match self.mode.as_ref() {
|
||||
Some(mode) => {
|
||||
|
||||
@@ -16,21 +16,26 @@ pub struct SearchPlaylistRequest {
|
||||
pub range: Option<WindowRange>,
|
||||
}
|
||||
|
||||
impl SearchPlaylistRequest {
|
||||
pub fn new(name: PlaylistName, filter: Filter, range: Option<WindowRange>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
filter,
|
||||
range,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRequest for SearchPlaylistRequest {
|
||||
const COMMAND: &'static str = "searchplaylist";
|
||||
const MIN_ARGS: u32 = 2;
|
||||
const MAX_ARGS: Option<u32> = Some(3);
|
||||
|
||||
fn into_request_enum(self) -> crate::Request {
|
||||
crate::Request::SearchPlaylist(self.name, self.filter, self.range)
|
||||
}
|
||||
|
||||
fn from_request_enum(request: crate::Request) -> Option<Self> {
|
||||
match request {
|
||||
crate::Request::SearchPlaylist(name, filter, range) => Some(SearchPlaylistRequest {
|
||||
name,
|
||||
filter,
|
||||
range,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&self) -> String {
|
||||
let mut cmd = format!("{} {} {}", Self::COMMAND, self.name, self.filter);
|
||||
if let Some(range) = &self.range {
|
||||
|
||||
+1
-1
@@ -295,7 +295,7 @@ pub(super) fn unescape_string(s: &str) -> String {
|
||||
result
|
||||
}
|
||||
|
||||
lalrpop_mod!(filter_grammar, "/src/filter_grammar.rs");
|
||||
lalrpop_mod!(filter_grammar, "/filter_grammar.rs");
|
||||
|
||||
impl Filter {
|
||||
pub fn parse(input: &str) -> Result<Filter, RequestParserError> {
|
||||
|
||||
+5
-1
@@ -4,13 +4,17 @@
|
||||
//! It does not provide any client or server implementation
|
||||
|
||||
mod client;
|
||||
mod commands;
|
||||
mod request;
|
||||
mod request_tokenizer;
|
||||
mod response;
|
||||
mod response_tokenizer;
|
||||
mod server;
|
||||
|
||||
pub mod commands;
|
||||
pub mod filter;
|
||||
pub mod types;
|
||||
|
||||
pub use client::{MpdClient, MpdClientError};
|
||||
pub use request::Request;
|
||||
pub use response::Response;
|
||||
pub use server::MpdServer;
|
||||
|
||||
+587
@@ -0,0 +1,587 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::commands::*;
|
||||
use crate::types::*;
|
||||
|
||||
use crate::filter::Filter;
|
||||
use crate::request_tokenizer::RequestTokenizer;
|
||||
|
||||
// TODO: SingleLineString
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Request {
|
||||
CommandList(Vec<Request>),
|
||||
|
||||
// -- Query Commands -- //
|
||||
ClearError,
|
||||
CurrentSong,
|
||||
Idle(Option<Vec<SubSystem>>),
|
||||
Status,
|
||||
Stats,
|
||||
|
||||
// -- Playback Commands -- //
|
||||
Consume(BoolOrOneshot),
|
||||
Crossfade(Seconds),
|
||||
MixRampDb(f32),
|
||||
MixRampDelay(Seconds),
|
||||
Random(bool),
|
||||
Repeat(bool),
|
||||
SetVol(VolumeValue),
|
||||
GetVol,
|
||||
Single(BoolOrOneshot),
|
||||
ReplayGainMode(ReplayGainModeMode),
|
||||
ReplayGainStatus,
|
||||
Volume(VolumeValue),
|
||||
|
||||
// -- Playback Control Commands -- //
|
||||
Next,
|
||||
Pause(Option<bool>),
|
||||
Play(Option<SongPosition>),
|
||||
PlayId(Option<SongId>),
|
||||
Previous,
|
||||
Seek(SongPosition, TimeWithFractions),
|
||||
SeekId(SongId, TimeWithFractions),
|
||||
SeekCur(SeekMode, TimeWithFractions),
|
||||
Stop,
|
||||
|
||||
// -- Queue Commands -- //
|
||||
// TODO: relative mode
|
||||
Add(String, Option<SongPosition>),
|
||||
// TODO: relative mode
|
||||
AddId(String, Option<SongPosition>),
|
||||
Clear,
|
||||
Delete(OneOrRange),
|
||||
DeleteId(SongId),
|
||||
// TODO: account for relative moves
|
||||
Move(OneOrRange, AbsouluteRelativeSongPosition),
|
||||
// TODO: account for relative moves
|
||||
MoveId(SongId, AbsouluteRelativeSongPosition),
|
||||
Playlist,
|
||||
PlaylistFind(Filter, Option<Sort>, Option<WindowRange>),
|
||||
PlaylistId(SongId),
|
||||
PlaylistInfo(Option<OneOrRange>),
|
||||
PlaylistSearch(Filter, Option<Sort>, Option<WindowRange>),
|
||||
// TODO: which type of range?
|
||||
PlChanges(PlaylistVersion, Option<WindowRange>),
|
||||
// TODO: which type of range?
|
||||
PlChangesPosId(PlaylistVersion, Option<WindowRange>),
|
||||
// TODO: which type of range?
|
||||
Prio(Priority, WindowRange),
|
||||
PrioId(Priority, Vec<SongId>),
|
||||
RangeId(SongId, TimeInterval),
|
||||
Shuffle(Option<OneOrRange>),
|
||||
Swap(SongPosition, SongPosition),
|
||||
SwapId(SongId, SongId),
|
||||
AddTagId(SongId, TagName, TagValue),
|
||||
ClearTagId(SongId, TagName),
|
||||
|
||||
// -- Stored Playlist Commands -- //
|
||||
ListPlaylist(PlaylistName, Option<WindowRange>),
|
||||
ListPlaylistInfo(PlaylistName, Option<WindowRange>),
|
||||
SearchPlaylist(PlaylistName, Filter, Option<WindowRange>),
|
||||
ListPlaylists,
|
||||
Load(PlaylistName, Option<WindowRange>, Option<SongPosition>),
|
||||
PlaylistAdd(PlaylistName, Uri, Option<SongPosition>),
|
||||
PlaylistClear(PlaylistName),
|
||||
PlaylistDelete(PlaylistName, OneOrRange),
|
||||
PlaylistLength(PlaylistName),
|
||||
// TODO: which type of range?
|
||||
PlaylistMove(PlaylistName, Option<OneOrRange>, SongPosition),
|
||||
Rename(PlaylistName, PlaylistName),
|
||||
Rm(PlaylistName),
|
||||
Save(PlaylistName, Option<SaveMode>),
|
||||
|
||||
// -- Music Database Commands -- //
|
||||
AlbumArt(Uri, Offset),
|
||||
Count(Filter, Option<GroupType>),
|
||||
GetFingerprint(Uri),
|
||||
Find(Filter, Option<Sort>, Option<WindowRange>),
|
||||
FindAdd(
|
||||
Filter,
|
||||
Option<Sort>,
|
||||
Option<WindowRange>,
|
||||
Option<SongPosition>,
|
||||
),
|
||||
List(TagName, Option<Filter>, Vec<GroupType>, Option<WindowRange>),
|
||||
#[deprecated]
|
||||
ListAll(Option<Uri>),
|
||||
#[deprecated]
|
||||
ListAllInfo(Option<Uri>),
|
||||
ListFiles(Option<Uri>),
|
||||
LsInfo(Option<Uri>),
|
||||
ReadComments(Uri),
|
||||
ReadPicture(Uri, Offset),
|
||||
Search(Filter, Option<Sort>, Option<WindowRange>),
|
||||
SearchAdd(
|
||||
Filter,
|
||||
Option<Sort>,
|
||||
Option<WindowRange>,
|
||||
Option<SongPosition>,
|
||||
),
|
||||
SearchAddPl(
|
||||
PlaylistName,
|
||||
Filter,
|
||||
Option<Sort>,
|
||||
Option<WindowRange>,
|
||||
Option<SongPosition>,
|
||||
),
|
||||
SearchCount(Filter, Option<GroupType>),
|
||||
Update(Option<Uri>),
|
||||
Rescan(Option<Uri>),
|
||||
|
||||
// -- Mount and Neighbor Commands -- //
|
||||
Mount(MountPath, Uri),
|
||||
Unmount(MountPath),
|
||||
ListMounts,
|
||||
ListNeighbors,
|
||||
|
||||
// -- Sticker Commands -- //
|
||||
StickerGet(StickerType, Uri, String),
|
||||
StickerSet(StickerType, Uri, String, String),
|
||||
StickerInc(StickerType, Uri, String, usize),
|
||||
StickerDec(StickerType, Uri, String, usize),
|
||||
StickerDelete(StickerType, Uri, String),
|
||||
StickerList(StickerType, Uri),
|
||||
StickerFind(StickerType, Uri, String, Option<Sort>, Option<WindowRange>),
|
||||
StickerFindValue(
|
||||
StickerType,
|
||||
Uri,
|
||||
String,
|
||||
String,
|
||||
Option<Sort>,
|
||||
Option<WindowRange>,
|
||||
),
|
||||
StickerNames,
|
||||
StickerTypes,
|
||||
StickerNamesTypes(Option<StickerType>),
|
||||
|
||||
// -- Connection Commands -- //
|
||||
Close,
|
||||
Kill,
|
||||
Password(String),
|
||||
Ping,
|
||||
BinaryLimit(u64),
|
||||
TagTypes,
|
||||
TagTypesDisable(Vec<TagName>),
|
||||
TagTypesEnable(Vec<TagName>),
|
||||
TagTypesClear,
|
||||
TagTypesAll,
|
||||
TagTypesAvailable,
|
||||
TagTypesReset(Vec<TagName>),
|
||||
Protocol,
|
||||
ProtocolDisable(Vec<Feature>),
|
||||
ProtocolEnable(Vec<Feature>),
|
||||
ProtocolClear,
|
||||
ProtocolAll,
|
||||
ProtocolAvailable,
|
||||
|
||||
// -- Partition Commands -- //
|
||||
Partition(PartitionName),
|
||||
ListPartitions,
|
||||
NewPartition(PartitionName),
|
||||
DelPartition(PartitionName),
|
||||
MoveOutput(String),
|
||||
|
||||
// -- Audio Output Commands -- //
|
||||
DisableOutput(AudioOutputId),
|
||||
EnableOutput(AudioOutputId),
|
||||
ToggleOutput(AudioOutputId),
|
||||
Outputs,
|
||||
OutputSet(AudioOutputId, String, String),
|
||||
|
||||
// -- Reflection Commands -- //
|
||||
Config,
|
||||
Commands,
|
||||
NotCommands,
|
||||
UrlHandlers,
|
||||
Decoders,
|
||||
|
||||
// -- Client to Client Commands -- //
|
||||
Subscribe(ChannelName),
|
||||
Unsubscribe(ChannelName),
|
||||
Channels,
|
||||
ReadMessages,
|
||||
SendMessage(ChannelName, String),
|
||||
}
|
||||
|
||||
macro_rules! serialize_req {
|
||||
($request_enum:expr, $request_struct:ty) => {
|
||||
{
|
||||
if let Some(request) = <$request_struct as CommandRequest>::from_request_enum($request_enum) {
|
||||
return <$request_struct as CommandRequest>::serialize(&request);
|
||||
} else {
|
||||
panic!("Mismatched request enum and struct in serialize_request macro, this should never happen!");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! parse_req {
|
||||
($request_struct:ty, $parts:expr) => {
|
||||
<$request_struct>::parse($parts).map(|req| req.into_request_enum())
|
||||
};
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn serialize(self) -> String {
|
||||
match self {
|
||||
/* querying mpd status */
|
||||
Request::ClearError => serialize_req!(self, ClearErrorRequest),
|
||||
Request::CurrentSong => serialize_req!(self, CurrentSongRequest),
|
||||
Request::Idle(_) => serialize_req!(self, IdleRequest),
|
||||
Request::Status => serialize_req!(self, StatusRequest),
|
||||
Request::Stats => serialize_req!(self, StatsRequest),
|
||||
|
||||
// /* playback options */
|
||||
Request::Consume(_) => serialize_req!(self, ConsumeRequest),
|
||||
Request::Crossfade(_) => serialize_req!(self, CrossfadeRequest),
|
||||
Request::MixRampDb(_) => serialize_req!(self, MixRampDbRequest),
|
||||
Request::MixRampDelay(_) => serialize_req!(self, MixRampDelayRequest),
|
||||
Request::Random(_) => serialize_req!(self, RandomRequest),
|
||||
Request::Repeat(_) => serialize_req!(self, RepeatRequest),
|
||||
Request::SetVol(_) => serialize_req!(self, SetVolRequest),
|
||||
Request::GetVol => serialize_req!(self, GetVolRequest),
|
||||
Request::Single(_) => serialize_req!(self, SingleRequest),
|
||||
Request::ReplayGainMode(_) => serialize_req!(self, ReplayGainModeRequest),
|
||||
Request::ReplayGainStatus => serialize_req!(self, ReplayGainStatusRequest),
|
||||
Request::Volume(_) => serialize_req!(self, VolumeRequest),
|
||||
|
||||
/* playback control */
|
||||
Request::Next => serialize_req!(self, NextRequest),
|
||||
Request::Pause(_) => serialize_req!(self, PauseRequest),
|
||||
Request::Play(_) => serialize_req!(self, PlayRequest),
|
||||
Request::PlayId(_) => serialize_req!(self, PlayIdRequest),
|
||||
Request::Previous => serialize_req!(self, PreviousRequest),
|
||||
Request::Seek(_, _) => serialize_req!(self, SeekRequest),
|
||||
Request::SeekId(_, _) => serialize_req!(self, SeekIdRequest),
|
||||
Request::SeekCur(_, _) => serialize_req!(self, SeekCurRequest),
|
||||
Request::Stop => serialize_req!(self, StopRequest),
|
||||
|
||||
/* queue */
|
||||
Request::Add(_, _) => serialize_req!(self, AddRequest),
|
||||
Request::AddId(_, _) => serialize_req!(self, AddIdRequest),
|
||||
Request::Clear => serialize_req!(self, ClearRequest),
|
||||
Request::Delete(_) => serialize_req!(self, DeleteRequest),
|
||||
Request::DeleteId(_) => serialize_req!(self, DeleteIdRequest),
|
||||
Request::Move(_, _) => serialize_req!(self, MoveRequest),
|
||||
Request::MoveId(_, _) => serialize_req!(self, MoveIdRequest),
|
||||
Request::Playlist => serialize_req!(self, PlaylistRequest),
|
||||
Request::PlaylistFind(_, _, _) => serialize_req!(self, PlaylistFindRequest),
|
||||
Request::PlaylistId(_) => serialize_req!(self, PlaylistIdRequest),
|
||||
Request::PlaylistInfo(_) => serialize_req!(self, PlaylistInfoRequest),
|
||||
Request::PlaylistSearch(_, _, _) => serialize_req!(self, PlaylistSearchRequest),
|
||||
Request::PlChanges(_, _) => serialize_req!(self, PlChangesRequest),
|
||||
Request::PlChangesPosId(_, _) => serialize_req!(self, PlChangesPosIdRequest),
|
||||
Request::Prio(_, _) => serialize_req!(self, PrioRequest),
|
||||
Request::PrioId(_, _) => serialize_req!(self, PrioIdRequest),
|
||||
Request::RangeId(_, _) => serialize_req!(self, RangeIdRequest),
|
||||
Request::Shuffle(_) => serialize_req!(self, ShuffleRequest),
|
||||
Request::Swap(_, _) => serialize_req!(self, SwapRequest),
|
||||
Request::SwapId(_, _) => serialize_req!(self, SwapIdRequest),
|
||||
Request::AddTagId(_, _, _) => serialize_req!(self, AddTagIdRequest),
|
||||
Request::ClearTagId(_, _) => serialize_req!(self, ClearTagIdRequest),
|
||||
|
||||
/* stored playlists */
|
||||
Request::ListPlaylist(_, _) => serialize_req!(self, ListPlaylistRequest),
|
||||
Request::ListPlaylistInfo(_, _) => serialize_req!(self, ListPlaylistInfoRequest),
|
||||
Request::SearchPlaylist(_, _, _) => serialize_req!(self, SearchPlaylistRequest),
|
||||
Request::ListPlaylists => serialize_req!(self, ListPlaylistsRequest),
|
||||
Request::Load(_, _, _) => serialize_req!(self, LoadRequest),
|
||||
Request::PlaylistAdd(_, _, _) => serialize_req!(self, PlaylistAddRequest),
|
||||
Request::PlaylistClear(_) => serialize_req!(self, PlaylistClearRequest),
|
||||
Request::PlaylistDelete(_, _) => serialize_req!(self, PlaylistDeleteRequest),
|
||||
Request::PlaylistLength(_) => serialize_req!(self, PlaylistLengthRequest),
|
||||
Request::PlaylistMove(_, _, _) => serialize_req!(self, PlaylistMoveRequest),
|
||||
Request::Rename(_, _) => serialize_req!(self, RenameRequest),
|
||||
Request::Rm(_) => serialize_req!(self, RmRequest),
|
||||
Request::Save(_, _) => serialize_req!(self, SaveRequest),
|
||||
|
||||
/* music database */
|
||||
Request::AlbumArt(_, _) => serialize_req!(self, AlbumArtRequest),
|
||||
Request::Count(_, _) => serialize_req!(self, CountRequest),
|
||||
Request::GetFingerprint(_) => serialize_req!(self, GetFingerprintRequest),
|
||||
Request::Find(_, _, _) => serialize_req!(self, FindRequest),
|
||||
Request::FindAdd(_, _, _, _) => serialize_req!(self, FindAddRequest),
|
||||
Request::List(_, _, _, _) => serialize_req!(self, ListRequest),
|
||||
Request::ListAll(_) => serialize_req!(self, ListAllRequest),
|
||||
Request::ListAllInfo(_) => serialize_req!(self, ListAllInfoRequest),
|
||||
Request::ListFiles(_) => serialize_req!(self, ListFilesRequest),
|
||||
Request::LsInfo(_) => serialize_req!(self, LsInfoRequest),
|
||||
Request::ReadComments(_) => serialize_req!(self, ReadCommentsRequest),
|
||||
Request::ReadPicture(_, _) => serialize_req!(self, ReadPictureRequest),
|
||||
Request::Search(_, _, _) => serialize_req!(self, SearchRequest),
|
||||
Request::SearchAdd(_, _, _, _) => serialize_req!(self, SearchAddRequest),
|
||||
Request::SearchAddPl(_, _, _, _, _) => serialize_req!(self, SearchAddPlRequest),
|
||||
Request::SearchCount(_, _) => serialize_req!(self, SearchCountRequest),
|
||||
Request::Update(_) => serialize_req!(self, UpdateRequest),
|
||||
Request::Rescan(_) => serialize_req!(self, RescanRequest),
|
||||
|
||||
/* mounts and neighbors */
|
||||
Request::Mount(_, _) => serialize_req!(self, MountRequest),
|
||||
Request::Unmount(_) => serialize_req!(self, UnmountRequest),
|
||||
Request::ListMounts => serialize_req!(self, ListMountsRequest),
|
||||
Request::ListNeighbors => serialize_req!(self, ListNeighborsRequest),
|
||||
|
||||
/* stickers */
|
||||
Request::StickerGet(_, _, _) => serialize_req!(self, StickerGetRequest),
|
||||
Request::StickerSet(_, _, _, _) => serialize_req!(self, StickerSetRequest),
|
||||
Request::StickerInc(_, _, _, _) => serialize_req!(self, StickerIncRequest),
|
||||
Request::StickerDec(_, _, _, _) => serialize_req!(self, StickerDecRequest),
|
||||
Request::StickerDelete(_, _, _) => serialize_req!(self, StickerDeleteRequest),
|
||||
Request::StickerList(_, _) => serialize_req!(self, StickerListRequest),
|
||||
Request::StickerFind(_, _, _, _, _) => serialize_req!(self, StickerFindRequest),
|
||||
Request::StickerNames => serialize_req!(self, StickerNamesRequest),
|
||||
Request::StickerTypes => serialize_req!(self, StickerTypesRequest),
|
||||
Request::StickerNamesTypes(_) => serialize_req!(self, StickerNamesTypesRequest),
|
||||
|
||||
/* connection settings */
|
||||
Request::Close => serialize_req!(self, CloseRequest),
|
||||
Request::Kill => serialize_req!(self, KillRequest),
|
||||
Request::Password(_) => serialize_req!(self, PasswordRequest),
|
||||
Request::Ping => serialize_req!(self, PingRequest),
|
||||
Request::BinaryLimit(_) => serialize_req!(self, BinaryLimitRequest),
|
||||
Request::TagTypes => serialize_req!(self, TagTypesRequest),
|
||||
Request::TagTypesDisable(_) => serialize_req!(self, TagTypesDisableRequest),
|
||||
Request::TagTypesEnable(_) => serialize_req!(self, TagTypesEnableRequest),
|
||||
Request::TagTypesClear => serialize_req!(self, TagTypesClearRequest),
|
||||
Request::TagTypesAll => serialize_req!(self, TagTypesAllRequest),
|
||||
Request::TagTypesAvailable => serialize_req!(self, TagTypesAvailableRequest),
|
||||
Request::TagTypesReset(_) => serialize_req!(self, TagTypesResetRequest),
|
||||
Request::Protocol => serialize_req!(self, ProtocolRequest),
|
||||
Request::ProtocolDisable(_) => serialize_req!(self, ProtocolDisableRequest),
|
||||
Request::ProtocolEnable(_) => serialize_req!(self, ProtocolEnableRequest),
|
||||
Request::ProtocolClear => serialize_req!(self, ProtocolClearRequest),
|
||||
Request::ProtocolAll => serialize_req!(self, ProtocolAllRequest),
|
||||
Request::ProtocolAvailable => serialize_req!(self, ProtocolAvailableRequest),
|
||||
|
||||
/* partition commands */
|
||||
Request::Partition(_) => serialize_req!(self, PartitionRequest),
|
||||
Request::ListPartitions => serialize_req!(self, ListPartitionsRequest),
|
||||
Request::NewPartition(_) => serialize_req!(self, NewPartitionRequest),
|
||||
Request::DelPartition(_) => serialize_req!(self, DelPartitionRequest),
|
||||
Request::MoveOutput(_) => serialize_req!(self, MoveOutputRequest),
|
||||
|
||||
/* audio output devices */
|
||||
Request::DisableOutput(_) => serialize_req!(self, DisableOutputRequest),
|
||||
Request::EnableOutput(_) => serialize_req!(self, EnableOutputRequest),
|
||||
Request::ToggleOutput(_) => serialize_req!(self, ToggleOutputRequest),
|
||||
Request::Outputs => serialize_req!(self, OutputsRequest),
|
||||
Request::OutputSet(_, _, _) => serialize_req!(self, OutputSetRequest),
|
||||
|
||||
/* reflection */
|
||||
Request::Config => serialize_req!(self, ConfigRequest),
|
||||
Request::Commands => serialize_req!(self, CommandsRequest),
|
||||
Request::NotCommands => serialize_req!(self, NotCommandsRequest),
|
||||
Request::UrlHandlers => serialize_req!(self, UrlHandlersRequest),
|
||||
Request::Decoders => serialize_req!(self, DecodersRequest),
|
||||
|
||||
/* client to client */
|
||||
Request::Subscribe(_) => serialize_req!(self, SubscribeRequest),
|
||||
Request::Unsubscribe(_) => serialize_req!(self, UnsubscribeRequest),
|
||||
Request::Channels => serialize_req!(self, ChannelsRequest),
|
||||
Request::ReadMessages => serialize_req!(self, ReadMessagesRequest),
|
||||
Request::SendMessage(_, _) => serialize_req!(self, SendMessageRequest),
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: upon encountering an error, there should be a function that lets you skip to the next OK,
|
||||
// and continue execution. Maybe "parse_next_or_skip(&str) -> RequestParserResponse", which
|
||||
// could skip stuff internally? Or do we want to report back the error with the entire command
|
||||
// and let the library user decide what to do?
|
||||
//
|
||||
// TODO: should this return the remaining &str as well?
|
||||
pub fn parse_next(raw: &str) -> Result<Self, RequestParserError> {
|
||||
let (line, rest) = raw
|
||||
.split_once('\n')
|
||||
.ok_or(RequestParserError::UnexpectedEOF)?;
|
||||
let mut parts = RequestTokenizer::new(line);
|
||||
|
||||
match parts.next().ok_or(RequestParserError::EmptyLine)?.trim() {
|
||||
"command_list_begin" => {
|
||||
let mut commands = Vec::new();
|
||||
let mut i = 1;
|
||||
loop {
|
||||
i += 1;
|
||||
let (line, _rest) = rest
|
||||
.split_once('\n')
|
||||
.ok_or(RequestParserError::MissingCommandListEnd)?;
|
||||
match line.trim() {
|
||||
"command_list_begin" => {
|
||||
return Err(RequestParserError::NestedCommandList { line: i });
|
||||
}
|
||||
"command_list_end" => {
|
||||
return Ok(Request::CommandList(commands));
|
||||
}
|
||||
input => {
|
||||
let command = Request::parse_next(input)?;
|
||||
commands.push(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"command_list_end" => Err(RequestParserError::UnexpectedCommandListEnd),
|
||||
|
||||
/* querying mpd status */
|
||||
ClearErrorRequest::COMMAND => parse_req!(ClearErrorRequest, parts),
|
||||
CurrentSongRequest::COMMAND => parse_req!(CurrentSongRequest, parts),
|
||||
IdleRequest::COMMAND => parse_req!(IdleRequest, parts),
|
||||
StatusRequest::COMMAND => parse_req!(StatusRequest, parts),
|
||||
StatsRequest::COMMAND => parse_req!(StatsRequest, parts),
|
||||
|
||||
/* playback options */
|
||||
ConsumeRequest::COMMAND => parse_req!(ConsumeRequest, parts),
|
||||
CrossfadeRequest::COMMAND => parse_req!(CrossfadeRequest, parts),
|
||||
MixRampDbRequest::COMMAND => parse_req!(MixRampDbRequest, parts),
|
||||
MixRampDelayRequest::COMMAND => parse_req!(MixRampDelayRequest, parts),
|
||||
RandomRequest::COMMAND => parse_req!(RandomRequest, parts),
|
||||
RepeatRequest::COMMAND => parse_req!(RepeatRequest, parts),
|
||||
SetVolRequest::COMMAND => parse_req!(SetVolRequest, parts),
|
||||
GetVolRequest::COMMAND => parse_req!(GetVolRequest, parts),
|
||||
SingleRequest::COMMAND => parse_req!(SingleRequest, parts),
|
||||
ReplayGainModeRequest::COMMAND => parse_req!(ReplayGainModeRequest, parts),
|
||||
ReplayGainStatusRequest::COMMAND => parse_req!(ReplayGainStatusRequest, parts),
|
||||
VolumeRequest::COMMAND => parse_req!(VolumeRequest, parts),
|
||||
|
||||
/* playback control */
|
||||
NextRequest::COMMAND => parse_req!(NextRequest, parts),
|
||||
PauseRequest::COMMAND => parse_req!(PauseRequest, parts),
|
||||
PlayRequest::COMMAND => parse_req!(PlayRequest, parts),
|
||||
PlayIdRequest::COMMAND => parse_req!(PlayIdRequest, parts),
|
||||
PreviousRequest::COMMAND => parse_req!(PreviousRequest, parts),
|
||||
SeekRequest::COMMAND => parse_req!(SeekRequest, parts),
|
||||
SeekIdRequest::COMMAND => parse_req!(SeekIdRequest, parts),
|
||||
SeekCurRequest::COMMAND => parse_req!(SeekCurRequest, parts),
|
||||
StopRequest::COMMAND => parse_req!(StopRequest, parts),
|
||||
|
||||
/* queue */
|
||||
AddRequest::COMMAND => parse_req!(AddRequest, parts),
|
||||
AddIdRequest::COMMAND => parse_req!(AddIdRequest, parts),
|
||||
ClearRequest::COMMAND => parse_req!(ClearRequest, parts),
|
||||
DeleteRequest::COMMAND => parse_req!(DeleteRequest, parts),
|
||||
DeleteIdRequest::COMMAND => parse_req!(DeleteIdRequest, parts),
|
||||
MoveRequest::COMMAND => parse_req!(MoveRequest, parts),
|
||||
MoveIdRequest::COMMAND => parse_req!(MoveIdRequest, parts),
|
||||
PlaylistRequest::COMMAND => parse_req!(PlaylistRequest, parts),
|
||||
PlaylistFindRequest::COMMAND => parse_req!(PlaylistFindRequest, parts),
|
||||
PlaylistIdRequest::COMMAND => parse_req!(PlaylistIdRequest, parts),
|
||||
PlaylistInfoRequest::COMMAND => parse_req!(PlaylistInfoRequest, parts),
|
||||
PlaylistSearchRequest::COMMAND => parse_req!(PlaylistSearchRequest, parts),
|
||||
PlChangesRequest::COMMAND => parse_req!(PlChangesRequest, parts),
|
||||
PlChangesPosIdRequest::COMMAND => parse_req!(PlChangesPosIdRequest, parts),
|
||||
PrioRequest::COMMAND => parse_req!(PrioRequest, parts),
|
||||
PrioIdRequest::COMMAND => parse_req!(PrioIdRequest, parts),
|
||||
RangeIdRequest::COMMAND => parse_req!(RangeIdRequest, parts),
|
||||
ShuffleRequest::COMMAND => parse_req!(ShuffleRequest, parts),
|
||||
SwapRequest::COMMAND => parse_req!(SwapRequest, parts),
|
||||
SwapIdRequest::COMMAND => parse_req!(SwapIdRequest, parts),
|
||||
AddTagIdRequest::COMMAND => parse_req!(AddTagIdRequest, parts),
|
||||
ClearTagIdRequest::COMMAND => parse_req!(ClearTagIdRequest, parts),
|
||||
|
||||
/* stored playlists */
|
||||
ListPlaylistRequest::COMMAND => parse_req!(ListPlaylistRequest, parts),
|
||||
ListPlaylistInfoRequest::COMMAND => parse_req!(ListPlaylistInfoRequest, parts),
|
||||
SearchPlaylistRequest::COMMAND => parse_req!(SearchPlaylistRequest, parts),
|
||||
ListPlaylistsRequest::COMMAND => parse_req!(ListPlaylistsRequest, parts),
|
||||
LoadRequest::COMMAND => parse_req!(LoadRequest, parts),
|
||||
PlaylistAddRequest::COMMAND => parse_req!(PlaylistAddRequest, parts),
|
||||
PlaylistClearRequest::COMMAND => parse_req!(PlaylistClearRequest, parts),
|
||||
PlaylistDeleteRequest::COMMAND => parse_req!(PlaylistDeleteRequest, parts),
|
||||
PlaylistLengthRequest::COMMAND => parse_req!(PlaylistLengthRequest, parts),
|
||||
PlaylistMoveRequest::COMMAND => parse_req!(PlaylistMoveRequest, parts),
|
||||
RenameRequest::COMMAND => parse_req!(RenameRequest, parts),
|
||||
RmRequest::COMMAND => parse_req!(RmRequest, parts),
|
||||
SaveRequest::COMMAND => parse_req!(SaveRequest, parts),
|
||||
|
||||
/* music database */
|
||||
AlbumArtRequest::COMMAND => parse_req!(AlbumArtRequest, parts),
|
||||
CountRequest::COMMAND => parse_req!(CountRequest, parts),
|
||||
GetFingerprintRequest::COMMAND => parse_req!(GetFingerprintRequest, parts),
|
||||
FindRequest::COMMAND => parse_req!(FindRequest, parts),
|
||||
FindAddRequest::COMMAND => parse_req!(FindAddRequest, parts),
|
||||
ListRequest::COMMAND => parse_req!(ListRequest, parts),
|
||||
ListAllRequest::COMMAND => parse_req!(ListAllRequest, parts),
|
||||
ListAllInfoRequest::COMMAND => parse_req!(ListAllInfoRequest, parts),
|
||||
ListFilesRequest::COMMAND => parse_req!(ListFilesRequest, parts),
|
||||
LsInfoRequest::COMMAND => parse_req!(LsInfoRequest, parts),
|
||||
ReadCommentsRequest::COMMAND => parse_req!(ReadCommentsRequest, parts),
|
||||
ReadPictureRequest::COMMAND => parse_req!(ReadPictureRequest, parts),
|
||||
SearchRequest::COMMAND => parse_req!(SearchRequest, parts),
|
||||
SearchAddRequest::COMMAND => parse_req!(SearchAddRequest, parts),
|
||||
SearchAddPlRequest::COMMAND => parse_req!(SearchAddPlRequest, parts),
|
||||
SearchCountRequest::COMMAND => parse_req!(SearchCountRequest, parts),
|
||||
UpdateRequest::COMMAND => parse_req!(UpdateRequest, parts),
|
||||
RescanRequest::COMMAND => parse_req!(RescanRequest, parts),
|
||||
|
||||
/* mounts and neighbors */
|
||||
MountRequest::COMMAND => parse_req!(MountRequest, parts),
|
||||
UnmountRequest::COMMAND => parse_req!(UnmountRequest, parts),
|
||||
ListMountsRequest::COMMAND => parse_req!(ListMountsRequest, parts),
|
||||
ListNeighborsRequest::COMMAND => parse_req!(ListNeighborsRequest, parts),
|
||||
|
||||
/* stickers */
|
||||
StickerGetRequest::COMMAND => parse_req!(StickerGetRequest, parts),
|
||||
StickerSetRequest::COMMAND => parse_req!(StickerSetRequest, parts),
|
||||
StickerIncRequest::COMMAND => parse_req!(StickerIncRequest, parts),
|
||||
StickerDecRequest::COMMAND => parse_req!(StickerDecRequest, parts),
|
||||
StickerDeleteRequest::COMMAND => parse_req!(StickerDeleteRequest, parts),
|
||||
StickerListRequest::COMMAND => parse_req!(StickerListRequest, parts),
|
||||
StickerFindRequest::COMMAND => parse_req!(StickerFindRequest, parts),
|
||||
StickerNamesRequest::COMMAND => parse_req!(StickerNamesRequest, parts),
|
||||
StickerTypesRequest::COMMAND => parse_req!(StickerTypesRequest, parts),
|
||||
StickerNamesTypesRequest::COMMAND => parse_req!(StickerNamesTypesRequest, parts),
|
||||
|
||||
/* connection settings */
|
||||
CloseRequest::COMMAND => parse_req!(CloseRequest, parts),
|
||||
KillRequest::COMMAND => parse_req!(KillRequest, parts),
|
||||
PasswordRequest::COMMAND => parse_req!(PasswordRequest, parts),
|
||||
PingRequest::COMMAND => parse_req!(PingRequest, parts),
|
||||
BinaryLimitRequest::COMMAND => parse_req!(BinaryLimitRequest, parts),
|
||||
TagTypesRequest::COMMAND => parse_req!(TagTypesRequest, parts),
|
||||
TagTypesDisableRequest::COMMAND => parse_req!(TagTypesDisableRequest, parts),
|
||||
TagTypesEnableRequest::COMMAND => parse_req!(TagTypesEnableRequest, parts),
|
||||
TagTypesClearRequest::COMMAND => parse_req!(TagTypesClearRequest, parts),
|
||||
TagTypesAllRequest::COMMAND => parse_req!(TagTypesAllRequest, parts),
|
||||
TagTypesAvailableRequest::COMMAND => parse_req!(TagTypesAvailableRequest, parts),
|
||||
TagTypesResetRequest::COMMAND => parse_req!(TagTypesResetRequest, parts),
|
||||
ProtocolRequest::COMMAND => parse_req!(ProtocolRequest, parts),
|
||||
ProtocolDisableRequest::COMMAND => parse_req!(ProtocolDisableRequest, parts),
|
||||
ProtocolEnableRequest::COMMAND => parse_req!(ProtocolEnableRequest, parts),
|
||||
ProtocolClearRequest::COMMAND => parse_req!(ProtocolClearRequest, parts),
|
||||
ProtocolAllRequest::COMMAND => parse_req!(ProtocolAllRequest, parts),
|
||||
ProtocolAvailableRequest::COMMAND => parse_req!(ProtocolAvailableRequest, parts),
|
||||
|
||||
/* partition commands */
|
||||
PartitionRequest::COMMAND => parse_req!(PartitionRequest, parts),
|
||||
ListPartitionsRequest::COMMAND => parse_req!(ListPartitionsRequest, parts),
|
||||
NewPartitionRequest::COMMAND => parse_req!(NewPartitionRequest, parts),
|
||||
DelPartitionRequest::COMMAND => parse_req!(DelPartitionRequest, parts),
|
||||
MoveOutputRequest::COMMAND => parse_req!(MoveOutputRequest, parts),
|
||||
|
||||
/* audio output devices */
|
||||
DisableOutputRequest::COMMAND => parse_req!(DisableOutputRequest, parts),
|
||||
EnableOutputRequest::COMMAND => parse_req!(EnableOutputRequest, parts),
|
||||
ToggleOutputRequest::COMMAND => parse_req!(ToggleOutputRequest, parts),
|
||||
OutputsRequest::COMMAND => parse_req!(OutputsRequest, parts),
|
||||
OutputSetRequest::COMMAND => parse_req!(OutputSetRequest, parts),
|
||||
|
||||
/* reflection */
|
||||
ConfigRequest::COMMAND => parse_req!(ConfigRequest, parts),
|
||||
CommandsRequest::COMMAND => parse_req!(CommandsRequest, parts),
|
||||
NotCommandsRequest::COMMAND => parse_req!(NotCommandsRequest, parts),
|
||||
UrlHandlersRequest::COMMAND => parse_req!(UrlHandlersRequest, parts),
|
||||
DecodersRequest::COMMAND => parse_req!(DecodersRequest, parts),
|
||||
|
||||
/* client to client */
|
||||
SubscribeRequest::COMMAND => parse_req!(SubscribeRequest, parts),
|
||||
UnsubscribeRequest::COMMAND => parse_req!(UnsubscribeRequest, parts),
|
||||
ChannelsRequest::COMMAND => parse_req!(ChannelsRequest, parts),
|
||||
ReadMessagesRequest::COMMAND => parse_req!(ReadMessagesRequest, parts),
|
||||
SendMessageRequest::COMMAND => parse_req!(SendMessageRequest, parts),
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ use std::fmt::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
// See https://github.com/MusicPlayerDaemon/MPD/blob/7774c3369e1484dc5dec6d7d9572e0a57e9c5302/src/command/AllCommands.cxx#L67-L209
|
||||
pub type Response = Result<(), MpdError>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
||||
+89
-81
@@ -69,97 +69,105 @@ impl<'a> Iterator for ResponseAttributes<'a> {
|
||||
type Item = Result<(&'a str, GenericResponseValue<'a>), ResponseParserError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if self.cursor >= self.bytestring.len() {
|
||||
if self.cursor >= self.bytestring.len() {
|
||||
return Some(Err(ResponseParserError::UnexpectedEOF));
|
||||
}
|
||||
if self.bytestring[self.cursor..].starts_with(b"OK") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let remaining = &self.bytestring[self.cursor..];
|
||||
let newline_pos = remaining
|
||||
.iter()
|
||||
.position(|&b| b == b'\n')
|
||||
.unwrap_or(remaining.len());
|
||||
let line = &remaining[..newline_pos];
|
||||
|
||||
// Skip empty lines
|
||||
if line.is_empty() {
|
||||
self.cursor += newline_pos + 1;
|
||||
return self.next();
|
||||
}
|
||||
|
||||
// NOTE: it is important that this happens before any None returns,
|
||||
// so that the iterator advances despite errors.
|
||||
self.cursor += newline_pos + 1;
|
||||
|
||||
let mut keyval = line.splitn(2, |&b| b == b':');
|
||||
let key_bytes = keyval.next()?;
|
||||
|
||||
// TODO: should this be a proper runtime error?
|
||||
debug_assert!(!key_bytes.is_empty());
|
||||
debug_assert!(std::str::from_utf8(key_bytes).is_ok());
|
||||
let key = std::str::from_utf8(key_bytes).ok()?;
|
||||
|
||||
// In the case of binary data, the following value will be the byte count
|
||||
// in decimal, and the actual binary data will follow in the next N bytes,
|
||||
// followed by a newline.
|
||||
//
|
||||
// We parse the number and assign the binary data to the "binary" key.
|
||||
if key == "binary" {
|
||||
let byte_count = match keyval.next() {
|
||||
Some(count) => count.trim_ascii_start(),
|
||||
None => {
|
||||
// TODO: throw more specific error
|
||||
return Some(Err(ResponseParserError::UnexpectedEOF));
|
||||
}
|
||||
};
|
||||
let byte_count_str = match std::str::from_utf8(byte_count) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
// TODO: throw more specific error
|
||||
return Some(Err(ResponseParserError::SyntaxError(
|
||||
0,
|
||||
"Invalid byte count".to_string(),
|
||||
)));
|
||||
}
|
||||
};
|
||||
let byte_count: usize = match byte_count_str.parse() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
// TODO: throw more specific error
|
||||
return Some(Err(ResponseParserError::SyntaxError(
|
||||
0,
|
||||
"Invalid byte count".to_string(),
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let value_start = self.cursor;
|
||||
let value_end = self.cursor + byte_count;
|
||||
if value_end > self.bytestring.len() {
|
||||
return Some(Err(ResponseParserError::UnexpectedEOF));
|
||||
}
|
||||
let value_bytes = &self.bytestring[value_start..value_end];
|
||||
|
||||
if self.bytestring[self.cursor..].starts_with(b"OK") {
|
||||
return None;
|
||||
}
|
||||
debug_assert!(
|
||||
self.bytestring[value_end..]
|
||||
.iter()
|
||||
.next()
|
||||
.is_none_or(|&b| b == b'\n')
|
||||
);
|
||||
|
||||
let remaining = &self.bytestring[self.cursor..];
|
||||
let newline_pos = remaining
|
||||
.iter()
|
||||
.position(|&b| b == b'\n')
|
||||
.unwrap_or(remaining.len());
|
||||
// Skip the binary data and the following newline
|
||||
self.cursor = value_end + 1;
|
||||
|
||||
let line = &remaining[..newline_pos];
|
||||
Some(Ok((key, GenericResponseValue::Binary(value_bytes))))
|
||||
} else {
|
||||
let value_bytes = match keyval.next() {
|
||||
Some(v) => v.trim_ascii_start(),
|
||||
None => b"",
|
||||
};
|
||||
// TODO: this should be a proper runtime error, the specification
|
||||
// declares that all string values are UTF-8.
|
||||
debug_assert!(std::str::from_utf8(value_bytes).is_ok());
|
||||
let value_str = std::str::from_utf8(value_bytes).ok()?;
|
||||
|
||||
// NOTE: it is important that this happens before any further None returns,
|
||||
// so that the iterator advances despite errors.
|
||||
self.cursor += newline_pos + 1;
|
||||
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Some(parse_line(line, self));
|
||||
Some(Ok((key, GenericResponseValue::Text(value_str))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_line<'a>(
|
||||
line: &'a [u8],
|
||||
state: &mut ResponseAttributes<'a>,
|
||||
) -> Result<(&'a str, GenericResponseValue<'a>), ResponseParserError> {
|
||||
let mut parts = line.splitn(2, |&b| b == b':');
|
||||
|
||||
let key = parts
|
||||
.next()
|
||||
.filter(|k| !k.is_empty())
|
||||
.and_then(|k| std::str::from_utf8(k).ok())
|
||||
.ok_or_else(|| ResponseParserError::SyntaxError(0, "Invalid key".into()))?;
|
||||
|
||||
match key {
|
||||
"binary" => parse_binary(parts.next(), state).map(|v| (key, v)),
|
||||
_ => {
|
||||
let value = parts.next().unwrap_or(b"").trim_ascii_start();
|
||||
|
||||
let text = std::str::from_utf8(value)
|
||||
.map_err(|_| ResponseParserError::SyntaxError(0, "Invalid UTF-8".into()))?;
|
||||
|
||||
Ok((key, GenericResponseValue::Text(text)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_binary<'a>(
|
||||
count_bytes: Option<&[u8]>,
|
||||
state: &mut ResponseAttributes<'a>,
|
||||
) -> Result<GenericResponseValue<'a>, ResponseParserError> {
|
||||
// In the case of binary data, the following value will be the byte count
|
||||
// in decimal, and the actual binary data will follow in the next N bytes,
|
||||
// followed by a newline.
|
||||
//
|
||||
// We parse the number and assign the binary data to the "binary" key.
|
||||
let count = count_bytes
|
||||
.map(|b| b.trim_ascii_start())
|
||||
.and_then(|b| std::str::from_utf8(b).ok())
|
||||
.and_then(|s| s.parse::<usize>().ok())
|
||||
.ok_or_else(|| ResponseParserError::SyntaxError(0, "Invalid byte count".into()))?;
|
||||
|
||||
let start = state.cursor;
|
||||
let end = start + count;
|
||||
|
||||
if end > state.bytestring.len() {
|
||||
return Err(ResponseParserError::UnexpectedEOF);
|
||||
}
|
||||
|
||||
let bytes = &state.bytestring[start..end];
|
||||
|
||||
if state.bytestring.get(end).is_some_and(|&b| b != b'\n') {
|
||||
return Err(ResponseParserError::SyntaxError(
|
||||
0,
|
||||
"Missing newline".into(),
|
||||
));
|
||||
}
|
||||
|
||||
state.cursor = end + 1;
|
||||
|
||||
Ok(GenericResponseValue::Binary(bytes))
|
||||
}
|
||||
|
||||
impl<'a> From<ResponseAttributes<'a>>
|
||||
for Result<HashMap<&'a str, GenericResponseValue<'a>>, ResponseParserError>
|
||||
{
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
use crate::{Request, Response, types::SubSystem};
|
||||
|
||||
pub trait MpdServer {
|
||||
type Error;
|
||||
|
||||
fn route_request(&mut self, request: Request) -> Result<Response, Self::Error> {
|
||||
match request {
|
||||
Request::ClearError => self.handle_clear_error().map(|_| Ok(())),
|
||||
Request::CurrentSong => self.handle_current_song().map(|_| Ok(())),
|
||||
Request::Idle(subsystems) => self.handle_idle(subsystems).map(|_| Ok(())),
|
||||
Request::Status => self.handle_status().map(|_| Ok(())),
|
||||
Request::Stats => self.handle_stats().map(|_| Ok(())),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_unimplemented(&mut self) -> Result<(), Self::Error> {
|
||||
// fn handle_unimplemented(&mut self, name: &str) -> Result<(), Self::Error> {
|
||||
// return Err("a".into());
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// -- Query Commands -- //
|
||||
fn handle_clear_error(&mut self) -> Result<(), Self::Error> {
|
||||
self.handle_unimplemented()
|
||||
}
|
||||
fn handle_current_song(&mut self) -> Result<(), Self::Error> {
|
||||
self.handle_unimplemented()
|
||||
}
|
||||
fn handle_idle(&mut self, _: Option<Vec<SubSystem>>) -> Result<(), Self::Error> {
|
||||
self.handle_unimplemented()
|
||||
}
|
||||
fn handle_status(&mut self) -> Result<(), Self::Error> {
|
||||
self.handle_unimplemented()
|
||||
}
|
||||
fn handle_stats(&mut self) -> Result<(), Self::Error> {
|
||||
self.handle_unimplemented()
|
||||
}
|
||||
|
||||
// -- Playback Commands -- //
|
||||
// handle_consume(ConsumeState),
|
||||
// handle_crossfade(Seconds),
|
||||
// handle_mix_ramp_db(f32),
|
||||
// handle_mix_ramp_delay(Seconds),
|
||||
// handle_random(bool),
|
||||
// handle_repeat(bool),
|
||||
// handle_set_vol(Volume),
|
||||
// handle_get_vol,
|
||||
// handle_single(SingleState),
|
||||
// handle_replay_gain_mode(ReplayGainMode),
|
||||
// handle_replay_gain_status,
|
||||
// handle_volume(Volume),
|
||||
|
||||
// // -- Playback Control Commands -- //
|
||||
// next,
|
||||
// pause(Option<bool>),
|
||||
// play(SongPosition),
|
||||
// play_id(SongId),
|
||||
// previous,
|
||||
// seek(SongPosition, TimeWithFractions),
|
||||
// seek_id(SongId, TimeWithFractions),
|
||||
// seek_cur(SeekMode, TimeWithFractions),
|
||||
// stop,
|
||||
|
||||
// // -- Queue Commands -- //
|
||||
// handle_add(String, Option<SongPosition>),
|
||||
// handle_add_id(String, Option<SongPosition>),
|
||||
// handle_clear,
|
||||
// handle_delete(OneOrRange),
|
||||
// handle_delete_id(SongId),
|
||||
// handle_move(OneOrRange, SongPosition),
|
||||
// handle_move_id(SongId, SongPosition),
|
||||
// handle_playlist,
|
||||
// handle_playlist_find(Filter, Option<Sort>, Option<WindowRange>),
|
||||
// handle_playlist_id(SongId),
|
||||
// handle_playlist_info(OneOrRange),
|
||||
// handle_playlist_search(Filter, Option<Sort>, Option<WindowRange>),
|
||||
// handle_pl_changes(Version, Option<WindowRange>),
|
||||
// handle_pl_changes_pos_id(Version, Option<WindowRange>),
|
||||
// handle_prio(Priority, WindowRange),
|
||||
// handle_prio_id(Priority, Vec<SongId>),
|
||||
// handle_range_id(SongId, WindowRange),
|
||||
// handle_shuffle(Option<OneOrRange>),
|
||||
// handle_swap(SongPosition, SongPosition),
|
||||
// handle_swap_id(SongId, SongId),
|
||||
// handle_add_tag_id(SongId, TagName, TagValue),
|
||||
// handle_clear_tag_id(SongId, TagName),
|
||||
|
||||
// // -- Stored Playlist Commands -- //
|
||||
// ListPlaylist(PlaylistName, Option<WindowRange>),
|
||||
// ListPlaylistInfo(PlaylistName, Option<WindowRange>),
|
||||
// SearchPlaylist(PlaylistName, Filter, Option<WindowRange>),
|
||||
// ListPlaylists,
|
||||
// Load(PlaylistName, Option<WindowRange>, SongPosition),
|
||||
// PlaylistAdd(PlaylistName, Uri, SongPosition),
|
||||
// PlaylistClear(PlaylistName),
|
||||
// PlaylistDelete(PlaylistName, OneOrRange),
|
||||
// PlaylistLength(PlaylistName),
|
||||
// // TODO: which type of range?
|
||||
// PlaylistMove(PlaylistName, OneOrRange, SongPosition),
|
||||
// Rename(PlaylistName, PlaylistName),
|
||||
// Rm(PlaylistName),
|
||||
// Save(PlaylistName, Option<SaveMode>),
|
||||
|
||||
// // -- Music Database Commands -- //
|
||||
// AlbumArt(Uri, Offset),
|
||||
// Count(Filter, Option<GroupType>),
|
||||
// GetFingerprint(Uri),
|
||||
// Find(Filter, Option<Sort>, Option<WindowRange>),
|
||||
// FindAdd(Filter, Option<Sort>, Option<WindowRange>, Option<SongPosition>),
|
||||
// List(Tag, Filter, Option<GroupType>),
|
||||
// #[deprecated]
|
||||
// ListAll(Option<Uri>),
|
||||
// #[deprecated]
|
||||
// ListAllInfo(Option<Uri>),
|
||||
// ListFiles(Uri),
|
||||
// LsInfo(Option<Uri>),
|
||||
// ReadComments(Uri),
|
||||
// ReadPicture(Uri, Offset),
|
||||
// Search(Filter, Option<Sort>, Option<WindowRange>),
|
||||
// SearchAdd(Filter, Option<Sort>, Option<WindowRange>, Option<SongPosition>),
|
||||
// SearchAddPl(Filter, Option<Sort>, Option<WindowRange>, Option<SongPosition>),
|
||||
// SearchCount(Filter, Option<GroupType>),
|
||||
// Update(Option<Uri>),
|
||||
// Rescan(Option<Uri>),
|
||||
|
||||
// // -- Mount and Neighbor Commands -- //
|
||||
// Mount(Option<Path>, Option<Uri>),
|
||||
// Unmount(Path),
|
||||
// ListMounts,
|
||||
// ListNeighbors,
|
||||
|
||||
// // -- Sticker Commands -- //
|
||||
// StickerGet(StickerType, Uri, String),
|
||||
// StickerSet(StickerType, Uri, String, String),
|
||||
// StickerDelete(StickerType, Uri, String),
|
||||
// StickerList(StickerType, Uri),
|
||||
// StickerFind(StickerType, Uri, String, Option<Sort>, Option<WindowRange>),
|
||||
// StickerFindValue(StickerType, Uri, String, String, Option<Sort>, Option<WindowRange>),
|
||||
// StickerNames,
|
||||
// StickerTypes,
|
||||
// StickerNamesTypes(Option<StickerType>),
|
||||
|
||||
// // -- Connection Commands -- //
|
||||
// Close,
|
||||
// Kill,
|
||||
// Password(String),
|
||||
// Ping,
|
||||
// BinaryLimit(u64),
|
||||
// TagTypes,
|
||||
// TagTypesDisable(Vec<Tag>),
|
||||
// TagTypesEnable(Vec<Tag>),
|
||||
// TagTypesClear,
|
||||
// TagTypesAll,
|
||||
// TagTypesAvailable,
|
||||
// TagTypesReset(Vec<Tag>),
|
||||
// Protocol,
|
||||
// ProtocolDisable(Vec<Feature>),
|
||||
// ProtocolEnable(Vec<Feature>),
|
||||
// ProtocolClear,
|
||||
// ProtocolAll,
|
||||
// ProtocolAvailable,
|
||||
|
||||
// // -- Partition Commands -- //
|
||||
// Partition(PartitionName),
|
||||
// ListPartitions,
|
||||
// NewPartition(PartitionName),
|
||||
// DelPartition(PartitionName),
|
||||
// MoveOutput(String),
|
||||
|
||||
// // -- Audio Output Commands -- //
|
||||
// DisableOutput(AudioOutputId),
|
||||
// EnableOutput(AudioOutputId),
|
||||
// ToggleOutput(AudioOutputId),
|
||||
// Outputs,
|
||||
// OutputSet(AudioOutputId, String, String),
|
||||
|
||||
// // -- Reflection Commands -- //
|
||||
// Config,
|
||||
// Commands,
|
||||
// NotCommands,
|
||||
// UrlHandlers,
|
||||
// Decoders,
|
||||
|
||||
// // -- Client to Client Commands -- //
|
||||
// Subscribe(ChannelName),
|
||||
// Unsubscribe(ChannelName),
|
||||
// Channels,
|
||||
// ReadMessages,
|
||||
// SendMessage(ChannelName, String),
|
||||
}
|
||||
|
||||
// impl Into<Vec<u8>> for Request {
|
||||
// fn into(self) -> Vec<u8> {
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
// pub enum SaveMode {
|
||||
// Create,
|
||||
// Append,
|
||||
// Replace,
|
||||
// }
|
||||
|
||||
// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
// pub enum SeekMode {
|
||||
// Relative,
|
||||
// RelativeReverse,
|
||||
// Absolute,
|
||||
// }
|
||||
|
||||
// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
// pub enum SubSystem {
|
||||
// /// The song database has been modified after update.
|
||||
// Database,
|
||||
// /// A database update has started or finished. If the database was modified during the update, the database event is also emitted.
|
||||
// Update,
|
||||
// /// A stored playlist has been modified, renamed, created or deleted
|
||||
// StoredPlaylist,
|
||||
// /// The queue (i.e. the current playlist) has been modified
|
||||
// Playlist,
|
||||
// /// The player has been started, stopped or seeked or tags of the currently playing song have changed (e.g. received from stream)
|
||||
// Player,
|
||||
// /// The volume has been changed
|
||||
// Mixer,
|
||||
// /// An audio output has been added, removed or modified (e.g. renamed, enabled or disabled)
|
||||
// Output,
|
||||
// /// Options like repeat, random, crossfade, replay gain
|
||||
// Options,
|
||||
// /// A partition was added, removed or changed
|
||||
// Partition,
|
||||
// /// The sticker database has been modified.
|
||||
// Sticker,
|
||||
// /// A client has subscribed or unsubscribed to a channel
|
||||
// Subscription,
|
||||
// /// A message was received on a channel this client is subscribed to; this event is only emitted when the client’s message queue is empty
|
||||
// Message,
|
||||
// /// A neighbor was found or lost
|
||||
// Neighbor,
|
||||
// /// The mount list has changed
|
||||
// Mount,
|
||||
|
||||
// /// Other subsystems not covered by the above
|
||||
// Other(String),
|
||||
// }
|
||||
Reference in New Issue
Block a user