147 Commits

Author SHA1 Message Date
oysteikt d27f07ce75 WIP: commands: add req/res constructors 2026-06-21 15:49:06 +09:00
oysteikt 81e2f73fda build.rs: fix lalrpop building in direnv
Build and test / check (push) Successful in 56s
Build and test / docs (push) Successful in 1m21s
Build and test / test (push) Successful in 1m41s
Build and test / build (push) Successful in 2m0s
2026-06-21 15:48:59 +09:00
oysteikt 2c0565624d commands: implement Default for empty req/res structs 2026-06-21 15:39:25 +09:00
oysteikt a5e6245957 commands: add ::new constructors for macro generated items 2026-06-21 15:39:25 +09:00
oysteikt 56d7c942db commands: remove toplevel Request/Response enums
This also exposes all the command types as public API
2026-06-21 15:39:23 +09:00
oysteikt e701aeab17 flake.lock: bump, Cargo.{toml,lock}: update inputs
Build and test / check (push) Successful in 1m4s
Build and test / build (push) Successful in 1m7s
Build and test / test (push) Successful in 1m38s
Build and test / docs (push) Successful in 1m40s
2026-04-02 14:06:22 +09:00
oysteikt 644020ea7f flake.lock: bump, Cargo.{toml,lock}: update inputs, fmt, lint
Build and test / build (push) Successful in 1m12s
Build and test / docs (push) Successful in 1m20s
Build and test / check (push) Successful in 1m32s
Build and test / test (push) Successful in 1m47s
2026-02-07 02:25:34 +09:00
oysteikt aafda77896 flake.nix: build package and run tests
Build and test / build (push) Successful in 1m47s
Build and test / test (push) Successful in 1m55s
Build and test / docs (push) Successful in 2m28s
Build and test / check (push) Failing after 39s
2026-01-25 15:06:44 +09:00
oysteikt b4a0b5e27f client: make better constructors/destructors 2026-01-25 15:06:18 +09:00
oysteikt 4dfb4d2150 commands/tag_types_disable: remove u32 import
Build and test / check (push) Successful in 1m4s
Build and test / build (push) Successful in 1m6s
Build and test / docs (push) Successful in 1m10s
Build and test / test (push) Successful in 1m46s
2026-01-10 01:22:17 +09:00
oysteikt e3300f6c8c response_tokenizer: refactor
Build and test / check (push) Successful in 1m5s
Build and test / build (push) Successful in 1m10s
Build and test / docs (push) Successful in 1m11s
Build and test / test (push) Successful in 2m2s
2026-01-10 01:16:30 +09:00
oysteikt a89ad2f93e commands: constructors for all commands
Build and test / build (push) Successful in 1m12s
Build and test / check (push) Successful in 1m22s
Build and test / test (push) Successful in 1m57s
Build and test / docs (push) Successful in 2m16s
2026-01-10 00:12:15 +09:00
oysteikt 7292a940d7 .cargo/config.toml: init with pvv-git registry
Build and test / check (push) Successful in 1m0s
Build and test / build (push) Successful in 1m39s
Build and test / test (push) Successful in 1m36s
Build and test / docs (push) Successful in 2m20s
2026-01-02 21:22:21 +09:00
oysteikt 1be0bdddc8 flake.lock: bump 2025-12-15 09:10:00 +09:00
oysteikt 20803c7573 .gitea/workflows: bump rsync-action from v1 -> v2 2025-12-15 09:08:42 +09:00
oysteikt 46e8a22ddb .gitea/workflows: run on debian-latest 2025-12-08 18:43:05 +09:00
oysteikt 695ef08f62 .gitea/workflows: update actions/checkout: v4 -> v6 2025-12-08 18:42:33 +09:00
oysteikt 4356258312 commands: store SubtypeParserError expected type as &str 2025-12-08 17:48:44 +09:00
oysteikt b1bbd4ebb1 commands: use new error variants for a few more commands 2025-12-08 17:30:47 +09:00
oysteikt f6a0489539 lib: fix doccomment url rendering 2025-12-08 16:27:55 +09:00
oysteikt cedf17d5f9 commands: add request parser error variant for keyword without value 2025-12-08 16:25:33 +09:00
oysteikt 350892fda9 commands: clearly define how arguments are counted in request error 2025-12-08 16:25:04 +09:00
oysteikt febe651158 Clean up misc. module doccomments 2025-12-08 16:15:12 +09:00
oysteikt 39e6b237af filter: document misc 2025-12-08 16:09:18 +09:00
oysteikt 65017ad6b3 .gitea/workflows: don't error on clippy warnings 2025-12-08 15:59:10 +09:00
oysteikt bc915d1ca7 .gitea/workflows: don't run with all features 2025-12-08 15:57:40 +09:00
oysteikt 44b0e6aa85 commands: use new error variants for various commands 2025-12-08 15:54:37 +09:00
oysteikt 670cdc7ba5 Preallocate a few more response parsers 2025-12-08 14:27:42 +09:00
oysteikt 0675d9d984 commands: add and fix variants for RequestParserError 2025-12-08 13:55:25 +09:00
oysteikt 27b1c889df filter: flatten module 2025-12-08 13:32:18 +09:00
oysteikt 8f3dff30a8 cargo fmt + clippy 2025-12-08 13:28:45 +09:00
oysteikt 04e8c1c144 commands: extend parser errors 2025-12-08 13:28:17 +09:00
oysteikt 2582ffac2a Remove a few unused type aliases 2025-12-08 13:11:09 +09:00
oysteikt 1bd2e4f0fb *_tokenizer: add module doccomment 2025-12-08 13:10:31 +09:00
oysteikt 818f89859f commands: force external users to interact with requests and responses through Command trait 2025-12-08 13:07:11 +09:00
oysteikt f2e2eb271a commands: add command executor directly on Command trait 2025-12-08 13:05:30 +09:00
oysteikt 10fb43fa64 examples/mpd-client: add some code 2025-12-08 13:00:46 +09:00
oysteikt d123a53828 commands: strip lifetimes 2025-12-08 12:30:19 +09:00
oysteikt 1311b8118f commands: add newline at end of all command serializers 2025-12-08 05:49:54 +09:00
oysteikt 9f74b219d3 client: init 2025-12-08 05:32:49 +09:00
oysteikt 143dc48019 MpdError: impl thiserror 2025-12-08 05:31:43 +09:00
oysteikt e8f2cd2034 make ResponseParserError self-contained, impl thiserror 2025-12-08 05:31:13 +09:00
oysteikt 72923b1549 common/types: flatten to types 2025-12-08 04:14:32 +09:00
oysteikt 3faf01355f commands: document module 2025-12-08 04:07:20 +09:00
oysteikt 4c07b966b7 response: add some notes about the errors 2025-12-08 01:46:14 +09:00
oysteikt edd9fd0958 LICENSE: init 2025-12-08 01:15:23 +09:00
oysteikt 874fac6ea5 cargo clippy 2025-12-08 01:08:58 +09:00
oysteikt 7e749a4f46 commands/stickernamestypes: add missing derive for response 2025-12-08 01:05:24 +09:00
oysteikt 525b913f70 commands/listplaylist: add note about format 2025-12-08 01:03:23 +09:00
oysteikt 9e3b2d0465 commands/seekcur: fix parser 2025-12-08 01:03:11 +09:00
oysteikt 58f7c01082 commands: fix request enum conversion for unmount 2025-12-08 00:52:27 +09:00
oysteikt b3b7d97fee flake.lock: bump inputs 2025-12-08 00:45:53 +09:00
oysteikt 5823e95eb9 commands: add response types for multiple commands 2025-12-08 00:44:37 +09:00
oysteikt c0cc63503a commands/currentsong: implement response parser 2025-12-08 00:29:00 +09:00
oysteikt b8aaac544d commands/listplaylistinfo: fix response type 2025-12-08 00:28:38 +09:00
oysteikt 2a97914c93 commands: split Command trait into req + res parts 2025-12-05 22:54:01 +09:00
oysteikt 6191e10028 cargo fmt 2025-12-07 21:39:57 +09:00
oysteikt fc5cbe8024 commands/listplaylistinfo: add response type 2025-12-07 21:33:01 +09:00
oysteikt c811094908 commands: implement all database selection responses 2025-12-07 21:33:02 +09:00
oysteikt 267fd0e2e9 types/tag: make orderable 2025-12-07 20:53:45 +09:00
oysteikt b97b650f64 common/types: add db selection print types 2025-12-07 21:21:58 +09:00
oysteikt a2c143a461 commands/listplaylists: implement 2025-12-07 20:53:45 +09:00
oysteikt d811840ea1 commands: parse to Self::Request 2025-12-05 22:00:11 +09:00
oysteikt 925eb1941a filter: export all types from inner module 2025-12-05 21:00:01 +09:00
oysteikt ef753e199a common: don't expose types directly 2025-12-05 20:59:19 +09:00
oysteikt cdb3b2d58c commands: add docs for Command trait 2025-11-25 05:28:58 +09:00
oysteikt 4f8fa26cd4 common/types: add better alias for MountPath 2025-11-25 04:27:20 +09:00
oysteikt 5117ed319d common/types: add better alias for AudioOutputId 2025-11-25 04:22:30 +09:00
oysteikt 69f79197aa common/types: add type for ChannelName 2025-11-25 04:13:32 +09:00
oysteikt f778d85a3c common/types: add better alias for PlaylistVersion 2025-11-25 04:02:03 +09:00
oysteikt e56b9ab6c4 common/types: add type for Sort 2025-11-25 03:56:01 +09:00
oysteikt 59a347f610 response_tokenizer: rewrite
This commit contains a rewrite of the response tokenizer, which
introduces lazy parsing of the response, handling of binary data, some
tests, as well as just generally more robustness against errors.
2025-11-24 23:49:27 +09:00
oysteikt bdbecf3e8f commands: fix request de/serialization for list 2025-11-24 22:00:42 +09:00
oysteikt 162b6f95f1 commands: remove some fixed TODOs 2025-11-24 21:59:42 +09:00
oysteikt e45d8c054b commands: split response tokenizer into separate file 2025-11-24 19:16:09 +09:00
oysteikt d5445466ff filter: implement basic parser 2025-11-24 17:38:37 +09:00
oysteikt 56bdeb3e0a common/types: case insensitive tags 2025-11-24 17:36:48 +09:00
oysteikt f3ad2fc73a Implement a proper request tokenizer 2025-11-24 14:18:30 +09:00
oysteikt 63dfa1b255 WIP: serialize requests 2025-11-21 16:48:55 +09:00
oysteikt 21f98c5808 filter: implement fmt::Display 2025-11-21 18:36:54 +09:00
oysteikt e4e7530902 common/types: implement serialization helpers for Tag 2025-11-21 18:36:18 +09:00
oysteikt 726ea92e1b common/types: implement fmt::Display 2025-11-21 16:59:39 +09:00
oysteikt 0bdfdf149e Cargo.toml: bump deps 2025-11-21 16:15:16 +09:00
oysteikt 07fdf4b631 flake.lock: bump 2025-11-21 16:14:13 +09:00
oysteikt fccb32ad6d commands: implement response parser for lsinfo 2025-11-21 16:13:09 +09:00
oysteikt 8967f38ffa cargo fmt + clippy 2025-11-21 16:02:15 +09:00
oysteikt 72247d3a9d filter: add unit tests 2025-11-21 15:49:27 +09:00
oysteikt 030449128a commands: implement response parser for tagtypes available 2025-11-21 15:16:30 +09:00
oysteikt cbf4e9d382 commands: implement response parser for protocol 2025-11-21 15:14:43 +09:00
oysteikt 79d974f9c0 commands: implement response parser for protocol available 2025-11-21 15:12:21 +09:00
oysteikt 95604aa20d commands: return runtime errors on invalid property names 2025-11-21 15:07:50 +09:00
oysteikt ebfe1311b9 commands: create result struct for readmessages 2025-11-21 14:55:57 +09:00
oysteikt d3e7fac92c commands: precalculate capacity and use iterators 2025-11-21 14:44:17 +09:00
oysteikt 5eb08d852a commands: make better use of expect_property_type! 2025-11-21 14:38:09 +09:00
oysteikt d66c555a26 commands: implement response parser for listmounts 2025-11-21 14:32:06 +09:00
oysteikt a58ed698e7 commands: implement common traits for responses 2025-11-21 14:18:44 +09:00
oysteikt fdd1d358cd commands: implement response parser for decoders 2025-11-21 14:14:25 +09:00
oysteikt 799d37bc0b commands: implement response parser for listneighbors 2025-11-21 14:04:12 +09:00
oysteikt cdad776df0 commands: implement response parser for outputs 2025-11-21 13:55:10 +09:00
oysteikt a261bdbf89 common/types: add debug assertions for range orders 2025-10-13 15:35:56 +09:00
oysteikt dcd8444a6a flake.lock: bump, Cargo.toml: update inputs 2025-10-12 23:01:17 +09:00
oysteikt 29f4e0519e commands: fix clippy warnings about confusing elided lifetimes 2025-10-12 22:59:50 +09:00
oysteikt 6d9e9cd468 flake.lock: bump, Cargo.toml: update inputs 2025-09-20 18:05:21 +02:00
oysteikt dcfe30e0fd .gitignore: ignore Cargo.lock
This is okay because this is a library
2025-09-20 18:03:35 +02:00
oysteikt 708849e87f flake.lock: bump 2025-08-03 05:01:37 +02:00
oysteikt ae0fbaaa15 .gitea/workflows: update gitea-web target host 2025-08-03 04:50:01 +02:00
oysteikt 8e10db40bd Cargo.toml: update deps 2025-07-11 20:37:16 +02:00
oysteikt aba16b3216 flake.lock: bump 2025-07-11 20:36:58 +02:00
oysteikt fe840015f4 flake.nix: add cargo-edit to devshell 2025-07-11 20:36:50 +02:00
oysteikt a1cea09979 flake.lock: bump 2025-05-07 10:43:24 +02:00
oysteikt de1cdd59ac Cargo.lock: bump 2025-05-05 10:15:00 +02:00
oysteikt 1e352d4413 flake.lock: bump 2025-05-05 10:13:44 +02:00
oysteikt 88e3deebc8 Rust edition 2024 2025-02-26 16:39:34 +01:00
oysteikt 98d1b1eebb common/types: move stuff from requests to types 2025-02-23 19:46:28 +01:00
oysteikt 05ae68de0b commands: deduplicate logic in macros, add more macros 2025-02-23 19:16:58 +01:00
oysteikt f6c76fb006 commands: implement some more response parsers 2025-02-23 16:41:15 +01:00
oysteikt 77d6ce919e Cargo fmt 2025-02-23 16:40:06 +01:00
oysteikt 11598a5827 commands: move ResponseAttributes parser out of Command 2025-02-23 16:33:34 +01:00
oysteikt 90a467287f flake.lock: bump 2025-02-23 16:30:39 +01:00
oysteikt 28c90b4802 common: move types into separate files 2025-02-23 16:30:21 +01:00
oysteikt 71af030116 Move project from Projects to Grzegorz 2025-01-06 16:32:13 +01:00
oysteikt 0b5fb4b571 commands: fix some syntax errors reporting literals 2024-12-14 00:19:25 +01:00
oysteikt c04c14e710 Implement some more commands 2024-12-14 00:14:02 +01:00
oysteikt 8dc6bb0382 commands: add missing debug asserts 2024-12-13 19:31:40 +01:00
oysteikt 98e6a05f9f commands: add some TODOs for assumptions made about syntax 2024-12-13 19:31:12 +01:00
oysteikt 7c26e2c409 response: remove leftover response types, add error codes 2024-12-13 18:29:54 +01:00
oysteikt 1e6279b7df Implement some more commands 2024-12-13 18:20:03 +01:00
oysteikt ace026af6b commands: add a few TODOs 2024-12-13 17:08:59 +01:00
oysteikt c62dfc58c9 format 2024-12-13 17:08:15 +01:00
oysteikt b03c0fac3c commands: verify key uniqueness for ResponseAttributes -> HashMap 2024-12-13 17:06:28 +01:00
oysteikt b4dfdb9b8b commands: use real Filter type 2024-12-13 17:05:08 +01:00
oysteikt ecbc645784 Move repo to Projects 2024-12-13 17:02:58 +01:00
oysteikt 642a46a7fc examples/mpd-client: format 2024-12-13 16:59:40 +01:00
oysteikt f6981052c8 .gitea/build-and-test: init 2024-12-10 21:48:20 +01:00
oysteikt 0ff66fb9d9 .envrc: init 2024-12-05 18:10:11 +01:00
oysteikt 7c91dda409 Add more commands 2024-12-01 20:06:01 +01:00
oysteikt d2ef02971f Add existing command parsers to the main request parser 2024-12-01 19:16:44 +01:00
oysteikt a7da84bc9c Implement some more commands 2024-11-30 17:12:49 +01:00
oysteikt b84cf514c2 prefer unimplemented! for unimplemented functions 2024-11-30 03:36:00 +01:00
oysteikt 28610b5235 commands: add derives for a few response types 2024-11-30 03:31:52 +01:00
oysteikt 94b42b52bc commands: handle some more response parsing 2024-11-30 03:28:22 +01:00
oysteikt 1b22b05dc6 commands/stats: parse_response 2024-11-30 02:28:29 +01:00
oysteikt d59b1bfb40 commands: make macros usable without use statements 2024-11-30 02:28:10 +01:00
oysteikt 48f7fc1faa commands: use different datastructure for response attrs 2024-11-30 02:18:06 +01:00
oysteikt 6c984cf322 flake.lock: bump 2024-11-30 01:57:57 +01:00
oysteikt a1ef15f41b Continued development 2024-11-30 01:57:45 +01:00
oysteikt 8b0d33b941 Initial commit 2024-11-05 22:47:35 +01:00
100 changed files with 2099 additions and 2267 deletions
+2
View File
@@ -0,0 +1,2 @@
[registries]
pvv-git = { index = "sparse+https://git.pvv.ntnu.no/api/packages/Grzegorz/cargo/" }
+5 -5
View File
@@ -7,7 +7,7 @@ on:
jobs:
build:
runs-on: debian-latest-slim
runs-on: debian-latest
steps:
- uses: actions/checkout@v6
@@ -18,7 +18,7 @@ jobs:
run: cargo build --verbose --release
check:
runs-on: debian-latest-slim
runs-on: debian-latest
steps:
- uses: actions/checkout@v6
@@ -35,7 +35,7 @@ jobs:
run: cargo clippy
test:
runs-on: debian-latest-slim
runs-on: debian-latest
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-slim
runs-on: debian-latest
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@v1
uses: https://git.pvv.ntnu.no/Projects/rsync-action@v2
with:
source: target/doc/
target: ${{ gitea.ref_name }}/docs/
-2
View File
@@ -1,5 +1,3 @@
/target
result
result-*
Cargo.lock
Generated
+900
View File
@@ -0,0 +1,900 @@
# 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"
+7 -6
View File
@@ -4,6 +4,7 @@ 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/"
@@ -11,13 +12,13 @@ edition = "2024"
rust-version = "1.85.0"
[dependencies]
chrono = { version = "0.4.42", features = ["serde"] }
futures-util = { version = "0.3.31", optional = true, features = ["io"] }
chrono = { version = "0.4.44", features = ["serde"] }
futures-util = { version = "0.3.32", 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.17"
tokio = { version = "1.48.0", optional = true, features = ["io-util"] }
thiserror = "2.0.18"
tokio = { version = "1.50.0", optional = true, features = ["io-util"] }
[features]
default = ["tokio"]
@@ -25,10 +26,10 @@ futures = ["dep:futures-util"]
tokio = ["dep:tokio"]
[dev-dependencies]
anyhow = "1.0.100"
anyhow = "1.0.102"
indoc = "2.0.7"
pretty_assertions = "1.4.1"
tokio = { version = "1.48.0", features = ["macros", "net", "rt"] }
tokio = { version = "1.50.0", features = ["macros", "net", "rt"] }
[build-dependencies]
lalrpop = "0.22.2"
+21
View File
@@ -0,0 +1,21 @@
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.
+6 -5
View File
@@ -1,8 +1,9 @@
fn main() {
lalrpop::process_root().unwrap();
// let debug_mode = std::env::var("PROFILE").unwrap() == "debug";
// lalrpop::Configuration::new()
// .emit_comments(debug_mode)
// .process()
// .unwrap();
lalrpop::Configuration::new()
//.emit_comments(debug_mode)
// .generate_in_source_tree()
.process()
.unwrap();
}
+5 -3
View File
@@ -4,9 +4,11 @@ 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);
println!("{}", client.read_initial_mpd_version().await?);
let mut client = MpdClient::new(&mut stream).await?;
println!(
"Connected to MPD server: {}",
client.get_mpd_version().unwrap_or("unknown")
);
client.play(None).await?;
Generated
+6 -6
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1764950072,
"narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=",
"lastModified": 1775036866,
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f61125a668a320878494449750330ca58b78c557",
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
"type": "github"
},
"original": {
@@ -29,11 +29,11 @@
]
},
"locked": {
"lastModified": 1765075567,
"narHash": "sha256-KFDCdQcHJ0hE3Nt5Gm5enRIhmtEifAjpxgUQ3mzSJpA=",
"lastModified": 1775099554,
"narHash": "sha256-3xBsGnGDLOFtnPZ1D3j2LU19wpAlYefRKTlkv648rU0=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "769156779b41e8787a46ca3d7d76443aaf68be6f",
"rev": "8d6387ed6d8e6e6672fd3ed4b61b59d44b124d99",
"type": "github"
},
"original": {
+41
View File
@@ -42,5 +42,46 @@
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;
};
});
};
}
+55 -22
View File
@@ -4,23 +4,25 @@
//! managing the playlist, and returns the expected response types directly
//! from its methods.
use crate::{Request, commands::*, types::SongPosition};
use crate::{commands::*, types::SongPosition};
#[cfg(feature = "futures")]
use futures_util::{
AsyncBufReadExt,
io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader},
io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter},
};
use thiserror::Error;
#[cfg(feature = "tokio")]
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufStream};
use thiserror::Error;
pub struct MpdClient<'a, T>
where
T: AsyncWrite + AsyncRead + Unpin,
{
connection: &'a mut T,
stream: BufStream<&'a mut T>,
mpd_version: Option<String>,
}
#[derive(Error, Debug)]
@@ -39,30 +41,53 @@ impl<'a, T> MpdClient<'a, T>
where
T: AsyncWrite + AsyncRead + Unpin,
{
pub fn new(connection: &'a mut T) -> Self {
MpdClient { connection }
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 async fn read_initial_mpd_version(&mut self) -> Result<String, MpdClientError> {
let mut reader = BufReader::new(&mut self.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> {
let mut version_line = String::new();
reader
self.stream
.read_line(&mut version_line)
.await
.map_err(MpdClientError::ConnectionError)?;
Ok(version_line.trim().to_string())
self.mpd_version = Some(version_line.trim().to_string());
Ok(())
}
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 = reader
let bytes_read = self
.stream
.read_until(b'\n', &mut line)
.await
.map_err(MpdClientError::ConnectionError)?;
@@ -81,25 +106,33 @@ where
Ok(response)
}
pub async fn play(
&mut self,
position: Option<SongPosition>,
) -> Result<PlayResponse, MpdClientError> {
let message = Request::Play(position);
let payload = message.serialize();
pub async fn execute<C>(&mut self, request: C::Request) -> Result<C::Response, MpdClientError>
where
C: Command,
{
let payload = request.serialize();
self.connection
self.stream
.write_all(payload.as_bytes())
.await
.map_err(MpdClientError::ConnectionError)?;
self.connection
self.stream
.flush()
.await
.map_err(MpdClientError::ConnectionError)?;
let response_bytes = self.read_response().await?;
let response = PlayResponse::parse_raw(&response_bytes)?;
let response = C::Response::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
}
}
+68 -179
View File
@@ -9,6 +9,9 @@
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;
@@ -34,22 +37,11 @@ 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(crate) trait CommandRequest
pub trait CommandRequest
where
Self: Sized,
{
@@ -96,14 +88,6 @@ 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;
@@ -116,7 +100,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)?;
@@ -145,18 +129,10 @@ where
}
/// A trait modelling a single MPD command response.
pub(crate) trait CommandResponse
pub 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>;
@@ -180,16 +156,6 @@ 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)
@@ -212,55 +178,6 @@ 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
@@ -272,9 +189,15 @@ macro_rules! empty_command_request {
pub struct [<$name Request>];
}
impl paste::paste! { [<$name Request>] } {
pub fn new() -> Self {
Self
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>] }
}
}
@@ -283,22 +206,6 @@ 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"
}
@@ -321,21 +228,19 @@ macro_rules! empty_command_response {
pub struct [<$name Response>];
}
impl paste::paste! { [<$name Response>] } {
pub fn new() -> Self {
Self
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 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> {
@@ -350,12 +255,20 @@ 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>] ($item_type);
pub struct [<$name Request>] (pub $item_type);
}
impl paste::paste! { [<$name Request>] } {
pub fn new(arg: $item_type) -> Self {
Self(arg)
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)
}
}
@@ -364,22 +277,6 @@ 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)
}
@@ -409,12 +306,20 @@ 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>] (Option<$item_type>);
pub struct [<$name Request>] (pub Option<$item_type>);
}
impl paste::paste! { [<$name Request>] } {
pub fn new(arg: Option<$item_type>) -> Self {
Self(arg)
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)
}
}
@@ -423,22 +328,6 @@ 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),
@@ -474,24 +363,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>] ( $item_type );
pub struct [<$name Response>] (pub $item_type);
}
impl paste::paste! { [<$name Response>] } {
pub fn new(arg: $item_type) -> Self {
Self(arg)
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)
}
}
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> {
@@ -524,24 +413,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>] ( Vec<$item_type> );
pub struct [<$name Response>] (pub Vec<$item_type>);
}
impl paste::paste! { [<$name Response>] } {
pub fn new(arg: Vec<$item_type>) -> Self {
Self(arg)
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)
}
}
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> {
+24 -8
View File
@@ -15,6 +15,12 @@ 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,
@@ -24,15 +30,25 @@ 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()?;
+1 -18
View File
@@ -16,7 +16,7 @@ pub struct OutputSetRequest {
}
impl OutputSetRequest {
fn new(output_id: AudioOutputId, attribute_name: String, attribute_value: String) -> Self {
pub fn new(output_id: AudioOutputId, attribute_name: String, attribute_value: String) -> Self {
Self {
output_id,
attribute_name,
@@ -30,23 +30,6 @@ 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!(
"{} {} {} {}",
+6 -8
View File
@@ -15,15 +15,13 @@ 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());
+12 -8
View File
@@ -13,21 +13,25 @@ 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);
+1 -14
View File
@@ -15,7 +15,7 @@ pub struct SendMessageRequest {
}
impl SendMessageRequest {
fn new(channel: ChannelName, message: String) -> Self {
pub fn new(channel: ChannelName, message: String) -> Self {
Self { channel, message }
}
}
@@ -25,19 +25,6 @@ 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 {
fn new(features: Vec<Feature>) -> Self {
Self(features)
pub fn new(features: Vec<Feature>) -> Self {
ProtocolDisableRequest(features)
}
}
@@ -19,17 +19,6 @@ 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 {
fn new(features: Vec<Feature>) -> Self {
Self(features)
pub fn new(features: Vec<Feature>) -> Self {
ProtocolEnableRequest(features)
}
}
@@ -19,17 +19,6 @@ 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,5 +1,3 @@
use std::u32;
use crate::{
commands::{Command, CommandRequest, RequestParserError, empty_command_response},
request_tokenizer::RequestTokenizer,
@@ -11,8 +9,8 @@ pub struct TagTypesDisable;
pub struct TagTypesDisableRequest(Vec<TagName>);
impl TagTypesDisableRequest {
fn new(tagnames: Vec<TagName>) -> Self {
Self(tagnames)
pub fn new(tag_types: Vec<TagName>) -> Self {
TagTypesDisableRequest(tag_types)
}
}
@@ -21,17 +19,6 @@ 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 {
fn new(tagnames: Vec<TagName>) -> Self {
Self(tagnames)
pub fn new(tag_types: Vec<TagName>) -> Self {
TagTypesEnableRequest(tag_types)
}
}
@@ -19,17 +19,6 @@ 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 {
fn new(tagnames: Vec<TagName>) -> Self {
Self(tagnames)
pub fn new(tag_types: Vec<TagName>) -> Self {
TagTypesResetRequest(tag_types)
}
}
@@ -19,17 +19,6 @@ 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!(
"{} {}",
+2 -13
View File
@@ -8,8 +8,8 @@ pub struct Pause;
pub struct PauseRequest(Option<bool>);
impl PauseRequest {
fn new(toggle: Option<bool>) -> Self {
Self(toggle)
pub fn new(state: Option<bool>) -> Self {
PauseRequest(state)
}
}
@@ -18,17 +18,6 @@ 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),
+6 -11
View File
@@ -14,22 +14,17 @@ 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)
}
+6 -11
View File
@@ -14,22 +14,17 @@ 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),
+6 -11
View File
@@ -14,22 +14,17 @@ 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,15 +14,13 @@ 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);
+6 -11
View File
@@ -14,22 +14,17 @@ 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!(
+6 -11
View File
@@ -10,22 +10,17 @@ 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!(
+12 -19
View File
@@ -17,22 +17,17 @@ 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)
}
@@ -69,15 +64,13 @@ 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()?;
+12 -19
View File
@@ -18,22 +18,17 @@ 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() {
@@ -84,15 +79,13 @@ 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()?;
+16 -23
View File
@@ -17,26 +17,21 @@ 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 {
@@ -111,15 +106,13 @@ 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()
+16 -16
View File
@@ -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,15 +17,13 @@ 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()?;
+22 -24
View File
@@ -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,15 +138,13 @@ 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!({
+6 -8
View File
@@ -16,15 +16,13 @@ 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)?;
+6 -8
View File
@@ -16,15 +16,13 @@ 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)?;
+6 -8
View File
@@ -15,15 +15,13 @@ 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()
+6 -8
View File
@@ -15,15 +15,13 @@ 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)?;
+6 -8
View File
@@ -15,15 +15,13 @@ 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()?;
+16 -19
View File
@@ -19,22 +19,17 @@ 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)
}
@@ -72,15 +67,17 @@ 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()?;
+6 -8
View File
@@ -19,15 +19,13 @@ 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()?;
+16 -23
View File
@@ -17,26 +17,21 @@ 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 {
@@ -111,15 +106,13 @@ 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()
+16 -16
View File
@@ -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 -25
View File
@@ -18,36 +18,29 @@ 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 {
+12 -21
View File
@@ -18,24 +18,17 @@ 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 {
@@ -85,15 +78,13 @@ 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()?;
+6 -8
View File
@@ -19,15 +19,13 @@ 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,6 +6,7 @@ use crate::{
pub struct MixRampDelay;
single_item_command_request!(MixRampDelay, "mixrampdelay", Seconds);
empty_command_response!(MixRampDelay);
impl Command for MixRampDelay {
+6 -11
View File
@@ -7,22 +7,17 @@ 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)
+6 -11
View File
@@ -7,22 +7,17 @@ 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,15 +17,13 @@ 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,15 +23,23 @@ 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()?;
+6 -11
View File
@@ -10,22 +10,17 @@ 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) => {
+22 -8
View File
@@ -24,15 +24,29 @@ 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()?;
+56 -8
View File
@@ -65,15 +65,63 @@ 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();
+6 -11
View File
@@ -14,22 +14,17 @@ 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),
+12 -19
View File
@@ -15,22 +15,17 @@ 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),
@@ -69,15 +64,13 @@ 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();
+10 -15
View File
@@ -15,26 +15,21 @@ 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",
+6 -13
View File
@@ -14,24 +14,17 @@ 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)
}
+6 -11
View File
@@ -14,22 +14,17 @@ 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)
}
+6 -11
View File
@@ -14,22 +14,17 @@ 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)
}
+7 -9
View File
@@ -13,16 +13,14 @@ 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 into_response_enum(self) -> crate::Response {
todo!()
}
fn from_response_enum(response: crate::Response) -> Option<Self> {
todo!()
}
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
unimplemented!()
}
}
+32 -24
View File
@@ -1,7 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::{
Response,
commands::{Command, CommandRequest, CommandResponse, RequestParserError, ResponseParserError},
filter::Filter,
request_tokenizer::RequestTokenizer,
@@ -18,26 +17,21 @@ 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 {
@@ -112,6 +106,12 @@ 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,15 +120,23 @@ 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!()
}
+23 -9
View File
@@ -13,6 +13,12 @@ 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,
@@ -21,16 +27,24 @@ 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 into_response_enum(self) -> crate::Response {
todo!()
}
fn from_response_enum(response: crate::Response) -> Option<Self> {
todo!()
}
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
unimplemented!()
}
}
+22 -8
View File
@@ -15,6 +15,12 @@ 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,
@@ -23,15 +29,23 @@ 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!()
}
+33 -24
View File
@@ -17,26 +17,21 @@ 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 {
@@ -111,6 +106,12 @@ 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,
@@ -119,16 +120,24 @@ 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 from_response_enum(response: crate::Response) -> Option<Self> {
todo!()
}
fn into_response_enum(self) -> crate::Response {
todo!()
}
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
unimplemented!()
}
}
+29 -22
View File
@@ -15,24 +15,17 @@ 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),
@@ -71,6 +64,12 @@ 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,
@@ -79,16 +78,24 @@ 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 from_response_enum(response: crate::Response) -> Option<Self> {
todo!()
}
fn into_response_enum(self) -> crate::Response {
todo!()
}
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
unimplemented!()
}
}
+19 -22
View File
@@ -15,24 +15,17 @@ 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),
@@ -71,6 +64,12 @@ 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'
@@ -78,16 +77,14 @@ pub struct PlChangesPosIdResponseEntry {
pub id: SongId,
}
impl PlChangesPosIdResponseEntry {
pub fn new(position: SongPosition, id: SongId) -> Self {
PlChangesPosIdResponseEntry { position, id }
}
}
impl CommandResponse for PlChangesPosIdResponse {
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> {
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
unimplemented!()
}
}
+6 -11
View File
@@ -14,22 +14,17 @@ 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)
}
+6 -11
View File
@@ -14,23 +14,18 @@ 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
+9 -14
View File
@@ -14,25 +14,20 @@ 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)
}
+6 -11
View File
@@ -14,22 +14,17 @@ 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)
}
+6 -11
View File
@@ -14,22 +14,17 @@ 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)
}
+10 -8
View File
@@ -18,15 +18,17 @@ 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 -8
View File
@@ -16,18 +16,26 @@ 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;
+11 -16
View File
@@ -16,27 +16,22 @@ 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",
+10 -15
View File
@@ -15,26 +15,21 @@ 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",
+30 -33
View File
@@ -18,36 +18,29 @@ 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!(
"{} {} {} {}",
@@ -151,6 +144,12 @@ 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,
@@ -158,15 +157,13 @@ 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);
+10 -15
View File
@@ -15,26 +15,21 @@ 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",
+11 -16
View File
@@ -16,27 +16,22 @@ 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",
+12 -21
View File
@@ -17,24 +17,17 @@ 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)
}
@@ -68,15 +61,13 @@ 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") {
+11 -16
View File
@@ -16,27 +16,22 @@ 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",
+12 -21
View File
@@ -14,24 +14,17 @@ 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)
@@ -59,15 +52,13 @@ 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);
+6 -11
View File
@@ -20,22 +20,17 @@ 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,24 +15,17 @@ 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),
@@ -63,16 +56,14 @@ 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 into_response_enum(self) -> crate::Response {
todo!()
}
fn from_response_enum(response: crate::Response) -> Option<Self> {
todo!()
}
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
unimplemented!()
}
}
+15 -8
View File
@@ -13,6 +13,12 @@ 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,
@@ -20,15 +26,16 @@ 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
+14 -15
View File
@@ -15,26 +15,25 @@ 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)) => {
+10 -15
View File
@@ -15,26 +15,21 @@ 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,27 +14,20 @@ 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,15 +18,13 @@ 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()?;
+10 -15
View File
@@ -15,26 +15,21 @@ 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) => {
+6 -13
View File
@@ -13,24 +13,17 @@ 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)
}
+9 -14
View File
@@ -14,25 +14,20 @@ 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) => {
+10 -15
View File
@@ -16,26 +16,21 @@ 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
View File
@@ -295,7 +295,7 @@ pub(super) fn unescape_string(s: &str) -> String {
result
}
lalrpop_mod!(filter_grammar, "/filter_grammar.rs");
lalrpop_mod!(filter_grammar, "/src/filter_grammar.rs");
impl Filter {
pub fn parse(input: &str) -> Result<Filter, RequestParserError> {
+1 -5
View File
@@ -4,17 +4,13 @@
//! 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
View File
@@ -1,587 +0,0 @@
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!(),
}
}
}
-1
View File
@@ -3,7 +3,6 @@ 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)]
+81 -89
View File
@@ -69,105 +69,97 @@ impl<'a> Iterator for ResponseAttributes<'a> {
type Item = Result<(&'a str, GenericResponseValue<'a>), ResponseParserError>;
fn next(&mut self) -> Option<Self::Item> {
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() {
loop {
if self.cursor >= self.bytestring.len() {
return Some(Err(ResponseParserError::UnexpectedEOF));
}
let value_bytes = &self.bytestring[value_start..value_end];
debug_assert!(
self.bytestring[value_end..]
.iter()
.next()
.is_none_or(|&b| b == b'\n')
);
if self.bytestring[self.cursor..].starts_with(b"OK") {
return None;
}
// Skip the binary data and the following newline
self.cursor = value_end + 1;
let remaining = &self.bytestring[self.cursor..];
let newline_pos = remaining
.iter()
.position(|&b| b == b'\n')
.unwrap_or(remaining.len());
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()?;
let line = &remaining[..newline_pos];
Some(Ok((key, GenericResponseValue::Text(value_str))))
// 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));
}
}
}
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
View File
@@ -1,246 +0,0 @@
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 clients 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),
// }