Compare commits

...

No commits in common. "main" and "gh-pages" have entirely different histories.

111 changed files with 2338 additions and 4286 deletions

View file

@ -1,4 +0,0 @@
avoid-breaking-exported-api = false
allowed-idents-below-min-chars = [ "x", "y", "z", "r", "g", "b", "c", "s" ]
absolute-paths-allowed-crates = [ "cstree" ]
allowed-wildcard-imports = [ "super", "Kind" ]

1
.envrc
View file

@ -1 +0,0 @@
use flake . --substituters "https://cache.nixos.org"

1
.github/CODEOWNERS vendored
View file

@ -1 +0,0 @@
* @NotAShelf

View file

@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View file

@ -1,67 +0,0 @@
name: Documentation
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: deploy
cancel-in-progress: false
jobs:
rustdoc:
name: Build API docs
runs-on: ubuntu-latest
env:
RUST_BACKTRACE: 1
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Configure cache
uses: Swatinem/rust-cache@v2
- name: Setup pages
id: pages
uses: actions/configure-pages@v6
- name: Build Documentation
run: |
cargo clean --doc ; cargo doc --no-deps --workspace --all-features
- name: Add redirect
run: echo '<meta http-equiv="refresh" content="0;url=mrc/index.html">' > target/doc/index.html
- name: Remove lock file
run: rm target/doc/.lock
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
path: target/doc
deploy:
name: Deploy Documentation
runs-on: ubuntu-latest
needs: rustdoc
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5

View file

@ -1,21 +0,0 @@
name: Build with Cargo
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
cargo-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Build
run: cargo build --verbose
- name: Test
run: cargo test --verbose

7
.gitignore vendored
View file

@ -1,7 +0,0 @@
/target
# Sensitive
certificate.pem
private_key.pem
identity.pfx

0
.lock Normal file
View file

0
.nojekyll Normal file
View file

View file

@ -1,5 +0,0 @@
edition = "2024" # Keep in sync with Cargo.toml.
group_imports = "StdExternalCrate"
doc_comment_code_block_width = 100
condense_wildcard_suffixes = true
imports_granularity = "Crate"

View file

@ -1,14 +0,0 @@
#:tombi schema.strict = false
[formatting]
align_entries = true
column_width = 110
compact_arrays = false
reorder_inline_tables = false
reorder_keys = true
[[rule]]
include = [ "**/Cargo.toml" ]
keys = [ "package" ]
[rule.formatting]
reorder_keys = false

1497
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,159 +0,0 @@
[package]
categories = [ "command-line-utilities", "multimedia" ]
default-run = "cli"
description = "MPV Remote Control - CLI and server for controlling MPV via IPC"
edition = "2024"
keywords = [ "mpv", "media", "player", "control", "ipc" ]
license = "MPL-2.0"
name = "mpvrc"
readme = true
repository = "https://github.com/notashelf/mpvrc"
rust-version = "1.92.0"
version = "0.3.0"
# CLI implementation for terminal usage
[[bin]]
name = "cli"
path = "src/cli.rs"
# Server implementation for remote usage
[[bin]]
name = "server"
path = "src/server.rs"
[dependencies]
anstyle = "1.0.14"
anyhow = "1.0.102"
clap = { version = "4.6.0", features = [ "derive", "cargo" ] }
clap_complete = "4.6.0"
clap_derive = "4.6.0"
dirs = "6.0.0"
ipc-channel = "0.21.0"
native-tls = "0.2.18"
rustyline = "18.0.0"
serde = { version = "1.0.228", features = [ "derive" ] }
serde_json = "1.0.149"
thiserror = "2.0.18"
tokio = { version = "1.50.0", features = [ "full" ] }
tokio-native-tls = "0.3.1"
tracing = "0.1.44"
tracing-subscriber = "0.3.23"
[profile.dev]
opt-level = 1
[profile.release]
codegen-units = 1
lto = "thin"
opt-level = "s"
panic = "abort"
strip = true
[lints.clippy]
cargo = { level = "warn", priority = -1 }
complexity = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
perf = { level = "warn", priority = -1 }
style = { level = "warn", priority = -1 }
# The lint groups above enable some less-than-desirable rules, we should manually
# enable those to keep our sanity.
absolute_paths = "allow"
alloc_instead_of_core = "allow"
allow_attributes_without_reason = "allow"
arbitrary_source_item_ordering = "allow"
arithmetic_side_effects = "allow"
as_conversions = "allow"
as_pointer_underscore = "allow"
as_underscore = "allow"
big_endian_bytes = "allow"
blanket_clippy_restriction_lints = "allow"
clone_on_ref_ptr = "allow"
dbg_macro = "allow"
disallowed_script_idents = "allow"
else_if_without_else = "allow"
empty_drop = "warn"
empty_structs_with_brackets = "warn"
error_impl_error = "allow"
exhaustive_enums = "allow"
exhaustive_structs = "allow"
exit = "warn"
expect_used = "allow"
field_scoped_visibility_modifiers = "allow"
filetype_is_file = "warn"
float_arithmetic = "allow"
get_unwrap = "warn"
host_endian_bytes = "allow"
impl_trait_in_params = "allow"
implicit_return = "allow"
indexing_slicing = "allow"
infinite_loop = "warn"
inline_asm_x86_intel_syntax = "allow"
integer_division = "allow"
integer_division_remainder_used = "allow"
large_include_file = "allow"
let_underscore_must_use = "allow"
let_underscore_untyped = "allow"
little_endian_bytes = "allow"
map_err_ignore = "allow"
map_with_unused_argument_over_ranges = "warn"
match_same_arms = "allow"
missing_assert_message = "allow"
missing_docs_in_private_items = "allow"
missing_errors_doc = "allow"
missing_inline_in_public_items = "allow"
missing_panics_doc = "allow"
missing_trait_methods = "allow"
mod_module_files = "allow"
multiple_crate_versions = "allow" # :(
multiple_inherent_impl = "allow"
mutex_atomic = "allow"
mutex_integer = "allow"
non_ascii_literal = "allow"
non_std_lazy_statics = "warn"
panic = "allow"
panic_in_result_fn = "allow"
partial_pub_fields = "allow"
pathbuf_init_then_push = "warn"
pattern_type_mismatch = "allow"
print_stderr = "allow"
print_stdout = "allow"
pub_use = "allow"
pub_with_shorthand = "allow"
pub_without_shorthand = "allow"
question_mark_used = "allow"
rc_buffer = "warn"
rc_mutex = "warn"
ref_patterns = "allow"
renamed_function_params = "allow"
rest_pat_in_fully_bound_structs = "warn"
same_name_method = "allow"
semicolon_outside_block = "allow"
separated_literal_suffix = "allow"
shadow_reuse = "allow"
shadow_same = "allow"
shadow_unrelated = "allow"
similar_names = "allow"
single_call_fn = "allow"
single_char_lifetime_names = "allow"
single_match_else = "allow"
std_instead_of_alloc = "allow"
std_instead_of_core = "allow"
string_add = "allow"
string_slice = "allow"
todo = "allow"
too_long_first_doc_paragraph = "allow"
too_many_lines = "allow"
try_err = "allow"
undocumented_unsafe_blocks = "warn"
unimplemented = "allow"
unnecessary_safety_comment = "allow"
unnecessary_safety_doc = "allow"
unreachable = "allow"
unused_result_ok = "warn"
unused_trait_names = "allow"
unwrap_in_result = "allow"
unwrap_used = "allow"
use_debug = "allow"
wildcard_enum_match_arm = "allow"

373
LICENSE
View file

@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

305
README.md
View file

@ -1,305 +0,0 @@
# mpvrc - MPV Remote Control
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)
[![crates.io](https://img.shields.io/crates/v/mpvrc.svg)](https://crates.io/crates/mpvrc)
[mpv video player]: https://mpv.io/
mpvrc is an unofficial command-line tool and library for controlling the
[mpv video player] via its JSON IPC socket interface. Generally designed as a
scriptable playback controller and a real-time interactive REPL in addition to a
TLS-encrypted HTTP API for _remote_ control. mpvrc has you covered for anything
that is even vaguely related to controlling a MPV instance.
## Features
The project is equipped with various neat features such as, but not limited to:
- **Full playback control**: play, pause, stop, seek, skip, and playlist
management
- **Interactive REPL**: vi-style editing, command history, and keyboard
shortcuts
- **HTTP server**: TLS-encrypted remote control with token authentication
- **Library API**: use MRC as a Rust crate in your own projects
It's also async-first, for high-performance concurrency.
## Prerequisites
1. **MPV** with IPC support (any recent version)
2. **Rust** 1.92.0+ (or Nix for shell-based development)
## Installation
### From source
```bash
# Clone and build using a recent cargo version
$ git clone https://github.com/notashelf/mpvrc.git
$ cd mpvrc
# Build in release mode
$ cargo build --release
```
### With Nix
The recommended way of building mpvrc is using Nix to acquire a developer shell.
```bash
# Clone and build using cargo from the provided shell
$ git clone https://github.com/notashelf/mpvrc.git
$ cd mpvrc
$ nix develop
$ cargo build --release
```
### From crates.io
You can also get mpvrc from <https://crates.io>.
```bash
# Install to ~/.cargo/bin
$ cargo install mpvrc --locked
```
## Quick Start
### 1. Start MPV with IPC
Using mpvrc is quite simple. Start `mpv` with an IPC socket first:
```bash
# `/tmp/mpvsocket` is the default socket path for mpvrc
$ mpv --idle --input-ipc-server=/tmp/mpvsocket
```
### 2. Control playback
Then, once the socket is up, you may use `mpvrc` to control MPV.
```bash
# Play/pause
$ mpvrc play
$ mpvrc pause
# Navigation
$ mpvrc next
$ mpvrc prev
# Seek 2 minutes forward
$ mpvrc seek 120
# Add files to playlist
$ mpvrc add ~/Videos/movie.mkv ~/Videos/episode1.mkv
# View playlist
$ mpvrc list
# Interactive mode
$ mpvrc interactive
```
## CLI Options
```plaintext
Usage: mpvrc [OPTIONS] <COMMAND>
Options:
-s, --socket <PATH> Path to MPV IPC socket [default: /tmp/mpvsocket]
-y, --yes Skip confirmation prompts for destructive commands
-d, --debug Enable debug logging
-h, --help Print help
-V, --version Print version
```
## Commands
<!--markdownlint-disable MD013-->
| Command | Description | Example |
| :------------------- | ------------------------------ | :----------------------------- |
| `play [index]` | Start/resume playback | `mpvrc play` or `mpvrc play 2` |
| `pause` | Pause playback | `mpvrc pause` |
| `stop` | Stop and quit MPV | `mpvrc stop` |
| `next` | Skip to next playlist item | `mpvrc next` |
| `prev` | Skip to previous item | `mpvrc prev` |
| `seek <seconds>` | Seek to position in seconds | `mpvrc seek 120` |
| `add <files...>` | Add files to playlist | `mpvrc add a.mp3 b.mp3` |
| `remove [index]` | Remove item (default: current) | `mpvrc remove 0` |
| `move <from> <to>` | Move playlist item | `mpvrc move 0 3` |
| `clear` | Clear playlist | `mpvrc clear` |
| `list` | Show playlist | `mpvrc list` |
| `prop <props...>` | Get property values | `mpvrc prop volume` |
| `interactive` | Enter interactive REPL | `mpvrc interactive` |
| `completion <shell>` | Generate shell completions | `mpvrc completion zsh` |
<!--markdownlint-enable MD013-->
### Shell Completions
Generate completions for your shell:
```bash
# Zsh
$ mpvrc completion zsh > ~/.zsh/completions/_mpvrc
# Bash
$ mpvrc completion bash > /etc/bash_completion.d/mpvrc
# Fish
$ mpvrc completion fish > ~/.config/fish/completions/mpvrc.fish
```
### Interactive Mode
```bash
$ mpvrc interactive
Entering interactive mode. Type 'help' for commands or 'exit' to quit.
Socket: /tmp/mpvsocket
mpv> play
mpv> seek 60
mpv> list
0 | Episode 1 | /path/to/episode1.mkv
> 1 | Episode 2 | /path/to/episode2.mkv
2 | Episode 3 | /path/to/episode3.mkv
mpv> exit
```
Keyboard shortcuts in interactive mode:
- `Ctrl+C` - Interrupt current command
- `Ctrl+L` - Clear screen
- `Ctrl+D` - Exit (EOF)
## Server Mode
Run MRC as a TLS-encrypted HTTP server for remote control:
### 1. Generate TLS certificates
```bash
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem
```
### 2. Configure environment
```bash
export TLS_PFX_PATH="./identity.pfx"
export TLS_PASSWORD="your_password"
export AUTH_TOKEN="your_secret_token"
```
### 3. Start server
```bash
mpvrc server --bind 127.0.0.1:8080 --socket /tmp/mpvsocket
```
### 4. Send commands
```bash
curl -k -H "Authorization: Bearer your_secret_token" \
-d "play" https://127.0.0.1:8080/
```
## Library Usage
Add MRC to your `Cargo.toml`:
```toml
[dependencies]
mpvrc = "0.2"
tokio = { version = "1", features = ["full"] }
serde_json = "1"
```
Control MPV from Rust:
```rust
use mpvrc::{get_property, playlist_next, set_property};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Set volume
set_property("volume", &json!(75), None).await?;
// Get current filename
if let Some(filename) = get_property("filename", None).await? {
println!("Now playing: {}", filename);
}
// Skip to next track
playlist_next(None).await?;
Ok(())
}
```
## Configuration
### Environment Variables
| Variable | Description | Required |
| -------------- | ------------------------------- | -------- |
| `TLS_PFX_PATH` | Path to PKCS#12 certificate | Server |
| `TLS_PASSWORD` | Password for PKCS#12 file | Server |
| `AUTH_TOKEN` | Bearer token for authentication | Server |
### Custom Socket Path
Use `-s` or `--socket` to specify a different MPV socket:
```bash
# As of 0.3.0, mvrc supports custom socket path
$ mpvrc -s /var/run/mpv/socket play
```
## Building & Development
The recommended developer setup for working with mpvrc is using Nix. Use
`nix develop` or `direnv allow` to enter a reproducible devshell with the
expected tooling, then work with Rust sources as usual:
```bash
# Build
$ cargo build
# Release build
$ cargo build --release
# Run tests
$ cargo test
# Lint
$ cargo clippy --all-targets
```
## Troubleshooting
### Socket not found
```bash
# Verify MPV is running with IPC
ls -la /tmp/mpvsocket
# Restart MPV with correct socket path
mpv --idle --input-ipc-server=/tmp/mpvsocket
```
### Debug output
```bash
mpvrc -d interactive
# or
RUST_LOG=debug mpvrc interactive
```
## License
This project is made available under Mozilla Public License (MPL) version 2.0.
See [LICENSE](LICENSE) for more details on the exact conditions. An online copy
is provided [here](https://www.mozilla.org/en-US/MPL/2.0/).

1
cli/all.html Normal file
View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="List of all items in this crate"><title>List of all items in this crate</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="cli" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../cli/index.html">cli</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h3><a href="#structs">Crate Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#enums" title="Enums">Enums</a></li><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><h1>List of all items</h1><h3 id="structs">Structs</h3><ul class="all-items"><li><a href="struct.Cli.html">Cli</a></li></ul><h3 id="enums">Enums</h3><ul class="all-items"><li><a href="enum.CommandOptions.html">CommandOptions</a></li></ul><h3 id="functions">Functions</h3><ul class="all-items"><li><a href="fn.main.html">main</a></li></ul></section></div></main></body></html>

File diff suppressed because one or more lines are too long

1
cli/fn.main.html Normal file
View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `main` fn in crate `cli`."><title>main in cli - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="cli" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../cli/index.html">cli</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">cli</a></div><h1>Function <span class="fn">main</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/cli/cli.rs.html#98-342">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) fn main() -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.unit.html">()</a>&gt;</code></pre></section></div></main></body></html>

1
cli/index.html Normal file
View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `cli` crate."><title>cli - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="cli" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="../crates.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod crate"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../cli/index.html">cli</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><ul class="block"><li><a id="all-types" href="all.html">All Items</a></li></ul><section id="rustdoc-toc"><h3><a href="#structs">Crate Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#enums" title="Enums">Enums</a></li><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1>Crate <span>cli</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/cli/cli.rs.html#1-342">Source</a> </span></div><h2 id="structs" class="section-header">Structs<a href="#structs" class="anchor">§</a></h2><dl class="item-table"><dt><a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a><span title="Restricted Visibility">&nbsp;🔒</span> </dt></dl><h2 id="enums" class="section-header">Enums<a href="#enums" class="anchor">§</a></h2><dl class="item-table"><dt><a class="enum" href="enum.CommandOptions.html" title="enum cli::CommandOptions">Command<wbr>Options</a><span title="Restricted Visibility">&nbsp;🔒</span> </dt></dl><h2 id="functions" class="section-header">Functions<a href="#functions" class="anchor">§</a></h2><dl class="item-table"><dt><a class="fn" href="fn.main.html" title="fn cli::main">main</a><span title="Restricted Visibility">&nbsp;🔒</span> </dt></dl></section></div></main></body></html>

1
cli/sidebar-items.js Normal file
View file

@ -0,0 +1 @@
window.SIDEBAR_ITEMS = {"enum":["CommandOptions"],"fn":["main"],"struct":["Cli"]};

37
cli/struct.Cli.html Normal file
View file

@ -0,0 +1,37 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `Cli` struct in crate `cli`."><title>Cli in cli - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="cli" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc struct"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../cli/index.html">cli</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">Cli</a></h2><h3><a href="#fields">Fields</a></h3><ul class="block structfield"><li><a href="#structfield.command" title="command">command</a></li><li><a href="#structfield.debug" title="debug">debug</a></li></ul><h3><a href="#trait-implementations">Trait Implementations</a></h3><ul class="block trait-implementation"><li><a href="#impl-Args-for-Cli" title="Args">Args</a></li><li><a href="#impl-CommandFactory-for-Cli" title="CommandFactory">CommandFactory</a></li><li><a href="#impl-FromArgMatches-for-Cli" title="FromArgMatches">FromArgMatches</a></li><li><a href="#impl-Parser-for-Cli" title="Parser">Parser</a></li></ul><h3><a href="#synthetic-implementations">Auto Trait Implementations</a></h3><ul class="block synthetic-implementation"><li><a href="#impl-Freeze-for-Cli" title="Freeze">Freeze</a></li><li><a href="#impl-RefUnwindSafe-for-Cli" title="RefUnwindSafe">RefUnwindSafe</a></li><li><a href="#impl-Send-for-Cli" title="Send">Send</a></li><li><a href="#impl-Sync-for-Cli" title="Sync">Sync</a></li><li><a href="#impl-Unpin-for-Cli" title="Unpin">Unpin</a></li><li><a href="#impl-UnwindSafe-for-Cli" title="UnwindSafe">UnwindSafe</a></li></ul><h3><a href="#blanket-implementations">Blanket Implementations</a></h3><ul class="block blanket-implementation"><li><a href="#impl-Any-for-T" title="Any">Any</a></li><li><a href="#impl-Borrow%3CT%3E-for-T" title="Borrow&#60;T&#62;">Borrow&#60;T&#62;</a></li><li><a href="#impl-BorrowMut%3CT%3E-for-T" title="BorrowMut&#60;T&#62;">BorrowMut&#60;T&#62;</a></li><li><a href="#impl-From%3CT%3E-for-T" title="From&#60;T&#62;">From&#60;T&#62;</a></li><li><a href="#impl-Instrument-for-T" title="Instrument">Instrument</a></li><li><a href="#impl-Into%3CU%3E-for-T" title="Into&#60;U&#62;">Into&#60;U&#62;</a></li><li><a href="#impl-TryFrom%3CU%3E-for-T" title="TryFrom&#60;U&#62;">TryFrom&#60;U&#62;</a></li><li><a href="#impl-TryInto%3CU%3E-for-T" title="TryInto&#60;U&#62;">TryInto&#60;U&#62;</a></li><li><a href="#impl-WithSubscriber-for-T" title="WithSubscriber">WithSubscriber</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate cli</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">cli</a></div><h1>Struct <span class="struct">Cli</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/cli/cli.rs.html#15-21">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) struct Cli {
pub(crate) debug: <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.bool.html">bool</a>,
pub(crate) command: <a class="enum" href="enum.CommandOptions.html" title="enum cli::CommandOptions">CommandOptions</a>,
}</code></pre><h2 id="fields" class="fields section-header">Fields<a href="#fields" class="anchor">§</a></h2><span id="structfield.debug" class="structfield section-header"><a href="#structfield.debug" class="anchor field">§</a><code>debug: <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.bool.html">bool</a></code></span><span id="structfield.command" class="structfield section-header"><a href="#structfield.command" class="anchor field">§</a><code>command: <a class="enum" href="enum.CommandOptions.html" title="enum cli::CommandOptions">CommandOptions</a></code></span><h2 id="trait-implementations" class="section-header">Trait Implementations<a href="#trait-implementations" class="anchor">§</a></h2><div id="trait-implementations-list"><details class="toggle implementors-toggle" open><summary><section id="impl-Args-for-Cli" class="impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#impl-Args-for-Cli" class="anchor">§</a><h3 class="code-header">impl Args for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.group_id" class="method trait-impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#method.group_id" class="anchor">§</a><h4 class="code-header">fn <a class="fn">group_id</a>() -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;Id&gt;</h4></section></summary><div class='docblock'>Report the [<code>ArgGroup::id</code>][crate::ArgGroup::id] for this set of arguments</div></details><details class="toggle method-toggle" open><summary><section id="method.augment_args" class="method trait-impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#method.augment_args" class="anchor">§</a><h4 class="code-header">fn <a class="fn">augment_args</a>&lt;'b&gt;(__clap_app: Command) -&gt; Command</h4></section></summary><div class='docblock'>Append to [<code>Command</code>] so it can instantiate <code>Self</code> via
[<code>FromArgMatches::from_arg_matches_mut</code>] <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.augment_args_for_update" class="method trait-impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#method.augment_args_for_update" class="anchor">§</a><h4 class="code-header">fn <a class="fn">augment_args_for_update</a>&lt;'b&gt;(__clap_app: Command) -&gt; Command</h4></section></summary><div class='docblock'>Append to [<code>Command</code>] so it can instantiate <code>self</code> via
[<code>FromArgMatches::update_from_arg_matches_mut</code>] <a>Read more</a></div></details></div></details><details class="toggle implementors-toggle" open><summary><section id="impl-CommandFactory-for-Cli" class="impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#impl-CommandFactory-for-Cli" class="anchor">§</a><h3 class="code-header">impl CommandFactory for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.command" class="method trait-impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#method.command" class="anchor">§</a><h4 class="code-header">fn <a class="fn">command</a>&lt;'b&gt;() -&gt; Command</h4></section></summary><div class='docblock'>Build a [<code>Command</code>] that can instantiate <code>Self</code>. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.command_for_update" class="method trait-impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#method.command_for_update" class="anchor">§</a><h4 class="code-header">fn <a class="fn">command_for_update</a>&lt;'b&gt;() -&gt; Command</h4></section></summary><div class='docblock'>Build a [<code>Command</code>] that can update <code>self</code>. <a>Read more</a></div></details></div></details><details class="toggle implementors-toggle" open><summary><section id="impl-FromArgMatches-for-Cli" class="impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#impl-FromArgMatches-for-Cli" class="anchor">§</a><h3 class="code-header">impl FromArgMatches for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.from_arg_matches" class="method trait-impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#method.from_arg_matches" class="anchor">§</a><h4 class="code-header">fn <a class="fn">from_arg_matches</a>(__clap_arg_matches: &amp;ArgMatches) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;Self, Error&gt;</h4></section></summary><div class='docblock'>Instantiate <code>Self</code> from [<code>ArgMatches</code>], parsing the arguments as needed. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.from_arg_matches_mut" class="method trait-impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#method.from_arg_matches_mut" class="anchor">§</a><h4 class="code-header">fn <a class="fn">from_arg_matches_mut</a>(
__clap_arg_matches: &amp;mut ArgMatches,
) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;Self, Error&gt;</h4></section></summary><div class='docblock'>Instantiate <code>Self</code> from [<code>ArgMatches</code>], parsing the arguments as needed. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.update_from_arg_matches" class="method trait-impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#method.update_from_arg_matches" class="anchor">§</a><h4 class="code-header">fn <a class="fn">update_from_arg_matches</a>(
&amp;mut self,
__clap_arg_matches: &amp;ArgMatches,
) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.unit.html">()</a>, Error&gt;</h4></section></summary><div class='docblock'>Assign values from <code>ArgMatches</code> to <code>self</code>.</div></details><details class="toggle method-toggle" open><summary><section id="method.update_from_arg_matches_mut" class="method trait-impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#method.update_from_arg_matches_mut" class="anchor">§</a><h4 class="code-header">fn <a class="fn">update_from_arg_matches_mut</a>(
&amp;mut self,
__clap_arg_matches: &amp;mut ArgMatches,
) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.unit.html">()</a>, Error&gt;</h4></section></summary><div class='docblock'>Assign values from <code>ArgMatches</code> to <code>self</code>.</div></details></div></details><details class="toggle implementors-toggle" open><summary><section id="impl-Parser-for-Cli" class="impl"><a class="src rightside" href="../src/cli/cli.rs.html#13">Source</a><a href="#impl-Parser-for-Cli" class="anchor">§</a><h3 class="code-header">impl Parser for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.parse" class="method trait-impl"><a href="#method.parse" class="anchor">§</a><h4 class="code-header">fn <a class="fn">parse</a>() -&gt; Self</h4></section></summary><div class='docblock'>Parse from <code>std::env::args_os()</code>, [exit][Error::exit] on error.</div></details><details class="toggle method-toggle" open><summary><section id="method.try_parse" class="method trait-impl"><a href="#method.try_parse" class="anchor">§</a><h4 class="code-header">fn <a class="fn">try_parse</a>() -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;Self, Error&gt;</h4></section></summary><div class='docblock'>Parse from <code>std::env::args_os()</code>, return Err on error.</div></details><details class="toggle method-toggle" open><summary><section id="method.parse_from" class="method trait-impl"><a href="#method.parse_from" class="anchor">§</a><h4 class="code-header">fn <a class="fn">parse_from</a>&lt;I, T&gt;(itr: I) -&gt; Self<div class="where">where
I: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/iter/traits/collect/trait.IntoIterator.html" title="trait core::iter::traits::collect::IntoIterator">IntoIterator</a>&lt;Item = T&gt;,
T: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;<a class="struct" href="https://doc.rust-lang.org/1.87.0/std/ffi/os_str/struct.OsString.html" title="struct std::ffi::os_str::OsString">OsString</a>&gt; + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</div></h4></section></summary><div class='docblock'>Parse from iterator, [exit][Error::exit] on error.</div></details><details class="toggle method-toggle" open><summary><section id="method.try_parse_from" class="method trait-impl"><a href="#method.try_parse_from" class="anchor">§</a><h4 class="code-header">fn <a class="fn">try_parse_from</a>&lt;I, T&gt;(itr: I) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;Self, Error&gt;<div class="where">where
I: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/iter/traits/collect/trait.IntoIterator.html" title="trait core::iter::traits::collect::IntoIterator">IntoIterator</a>&lt;Item = T&gt;,
T: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;<a class="struct" href="https://doc.rust-lang.org/1.87.0/std/ffi/os_str/struct.OsString.html" title="struct std::ffi::os_str::OsString">OsString</a>&gt; + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</div></h4></section></summary><div class='docblock'>Parse from iterator, return Err on error.</div></details><details class="toggle method-toggle" open><summary><section id="method.update_from" class="method trait-impl"><a href="#method.update_from" class="anchor">§</a><h4 class="code-header">fn <a class="fn">update_from</a>&lt;I, T&gt;(&amp;mut self, itr: I)<div class="where">where
I: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/iter/traits/collect/trait.IntoIterator.html" title="trait core::iter::traits::collect::IntoIterator">IntoIterator</a>&lt;Item = T&gt;,
T: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;<a class="struct" href="https://doc.rust-lang.org/1.87.0/std/ffi/os_str/struct.OsString.html" title="struct std::ffi::os_str::OsString">OsString</a>&gt; + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</div></h4></section></summary><div class='docblock'>Update from iterator, [exit][Error::exit] on error. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.try_update_from" class="method trait-impl"><a href="#method.try_update_from" class="anchor">§</a><h4 class="code-header">fn <a class="fn">try_update_from</a>&lt;I, T&gt;(&amp;mut self, itr: I) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.unit.html">()</a>, Error&gt;<div class="where">where
I: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/iter/traits/collect/trait.IntoIterator.html" title="trait core::iter::traits::collect::IntoIterator">IntoIterator</a>&lt;Item = T&gt;,
T: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;<a class="struct" href="https://doc.rust-lang.org/1.87.0/std/ffi/os_str/struct.OsString.html" title="struct std::ffi::os_str::OsString">OsString</a>&gt; + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</div></h4></section></summary><div class='docblock'>Update from iterator, return Err on error.</div></details></div></details></div><h2 id="synthetic-implementations" class="section-header">Auto Trait Implementations<a href="#synthetic-implementations" class="anchor">§</a></h2><div id="synthetic-implementations-list"><section id="impl-Freeze-for-Cli" class="impl"><a href="#impl-Freeze-for-Cli" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Freeze.html" title="trait core::marker::Freeze">Freeze</a> for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section><section id="impl-RefUnwindSafe-for-Cli" class="impl"><a href="#impl-RefUnwindSafe-for-Cli" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/panic/unwind_safe/trait.RefUnwindSafe.html" title="trait core::panic::unwind_safe::RefUnwindSafe">RefUnwindSafe</a> for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section><section id="impl-Send-for-Cli" class="impl"><a href="#impl-Send-for-Cli" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Send.html" title="trait core::marker::Send">Send</a> for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section><section id="impl-Sync-for-Cli" class="impl"><a href="#impl-Sync-for-Cli" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sync.html" title="trait core::marker::Sync">Sync</a> for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section><section id="impl-Unpin-for-Cli" class="impl"><a href="#impl-Unpin-for-Cli" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Unpin.html" title="trait core::marker::Unpin">Unpin</a> for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section><section id="impl-UnwindSafe-for-Cli" class="impl"><a href="#impl-UnwindSafe-for-Cli" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/panic/unwind_safe/trait.UnwindSafe.html" title="trait core::panic::unwind_safe::UnwindSafe">UnwindSafe</a> for <a class="struct" href="struct.Cli.html" title="struct cli::Cli">Cli</a></h3></section></div><h2 id="blanket-implementations" class="section-header">Blanket Implementations<a href="#blanket-implementations" class="anchor">§</a></h2><div id="blanket-implementations-list"><details class="toggle implementors-toggle"><summary><section id="impl-Any-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/any.rs.html#138">Source</a><a href="#impl-Any-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/any/trait.Any.html" title="trait core::any::Any">Any</a> for T<div class="where">where
T: 'static + ?<a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,</div></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.type_id" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/any.rs.html#139">Source</a><a href="#method.type_id" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/any/trait.Any.html#tymethod.type_id" class="fn">type_id</a>(&amp;self) -&gt; <a class="struct" href="https://doc.rust-lang.org/1.87.0/core/any/struct.TypeId.html" title="struct core::any::TypeId">TypeId</a></h4></section></summary><div class='docblock'>Gets the <code>TypeId</code> of <code>self</code>. <a href="https://doc.rust-lang.org/1.87.0/core/any/trait.Any.html#tymethod.type_id">Read more</a></div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-Borrow%3CT%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/borrow.rs.html#209">Source</a><a href="#impl-Borrow%3CT%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.Borrow.html" title="trait core::borrow::Borrow">Borrow</a>&lt;T&gt; for T<div class="where">where
T: ?<a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,</div></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.borrow" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/borrow.rs.html#211">Source</a><a href="#method.borrow" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.Borrow.html#tymethod.borrow" class="fn">borrow</a>(&amp;self) -&gt; <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.reference.html">&amp;T</a></h4></section></summary><div class='docblock'>Immutably borrows from an owned value. <a href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.Borrow.html#tymethod.borrow">Read more</a></div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-BorrowMut%3CT%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/borrow.rs.html#217">Source</a><a href="#impl-BorrowMut%3CT%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.BorrowMut.html" title="trait core::borrow::BorrowMut">BorrowMut</a>&lt;T&gt; for T<div class="where">where
T: ?<a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,</div></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.borrow_mut" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/borrow.rs.html#218">Source</a><a href="#method.borrow_mut" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.BorrowMut.html#tymethod.borrow_mut" class="fn">borrow_mut</a>(&amp;mut self) -&gt; <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.reference.html">&amp;mut T</a></h4></section></summary><div class='docblock'>Mutably borrows from an owned value. <a href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.BorrowMut.html#tymethod.borrow_mut">Read more</a></div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-From%3CT%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#767">Source</a><a href="#impl-From%3CT%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.From.html" title="trait core::convert::From">From</a>&lt;T&gt; for T</h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.from" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#770">Source</a><a href="#method.from" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.From.html#tymethod.from" class="fn">from</a>(t: T) -&gt; T</h4></section></summary><div class="docblock"><p>Returns the argument unchanged.</p>
</div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-Instrument-for-T" class="impl"><a href="#impl-Instrument-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; Instrument for T</h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.instrument" class="method trait-impl"><a href="#method.instrument" class="anchor">§</a><h4 class="code-header">fn <a class="fn">instrument</a>(self, span: Span) -&gt; Instrumented&lt;Self&gt;</h4></section></summary><div class='docblock'>Instruments this type with the provided [<code>Span</code>], returning an
<code>Instrumented</code> wrapper. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.in_current_span" class="method trait-impl"><a href="#method.in_current_span" class="anchor">§</a><h4 class="code-header">fn <a class="fn">in_current_span</a>(self) -&gt; Instrumented&lt;Self&gt;</h4></section></summary><div class='docblock'>Instruments this type with the <a href="super::Span::current()">current</a> <a href="crate::Span"><code>Span</code></a>, returning an
<code>Instrumented</code> wrapper. <a>Read more</a></div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-Into%3CU%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#750-752">Source</a><a href="#impl-Into%3CU%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T, U&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;U&gt; for T<div class="where">where
U: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.From.html" title="trait core::convert::From">From</a>&lt;T&gt;,</div></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.into" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#760">Source</a><a href="#method.into" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html#tymethod.into" class="fn">into</a>(self) -&gt; U</h4></section></summary><div class="docblock"><p>Calls <code>U::from(self)</code>.</p>
<p>That is, this conversion is whatever the implementation of
<code><a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.From.html" title="trait core::convert::From">From</a>&lt;T&gt; for U</code> chooses to do.</p>
</div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-TryFrom%3CU%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#806-808">Source</a><a href="#impl-TryFrom%3CU%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T, U&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;U&gt; for T<div class="where">where
U: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;T&gt;,</div></h3></section></summary><div class="impl-items"><details class="toggle" open><summary><section id="associatedtype.Error-1" class="associatedtype trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#810">Source</a><a href="#associatedtype.Error-1" class="anchor">§</a><h4 class="code-header">type <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#associatedtype.Error" class="associatedtype">Error</a> = <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/convert/enum.Infallible.html" title="enum core::convert::Infallible">Infallible</a></h4></section></summary><div class='docblock'>The type returned in the event of a conversion error.</div></details><details class="toggle method-toggle" open><summary><section id="method.try_from" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#813">Source</a><a href="#method.try_from" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#tymethod.try_from" class="fn">try_from</a>(value: U) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;T, &lt;T as <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;U&gt;&gt;::<a class="associatedtype" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#associatedtype.Error" title="type core::convert::TryFrom::Error">Error</a>&gt;</h4></section></summary><div class='docblock'>Performs the conversion.</div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-TryInto%3CU%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#791-793">Source</a><a href="#impl-TryInto%3CU%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T, U&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryInto.html" title="trait core::convert::TryInto">TryInto</a>&lt;U&gt; for T<div class="where">where
U: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;T&gt;,</div></h3></section></summary><div class="impl-items"><details class="toggle" open><summary><section id="associatedtype.Error" class="associatedtype trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#795">Source</a><a href="#associatedtype.Error" class="anchor">§</a><h4 class="code-header">type <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryInto.html#associatedtype.Error" class="associatedtype">Error</a> = &lt;U as <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;T&gt;&gt;::<a class="associatedtype" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#associatedtype.Error" title="type core::convert::TryFrom::Error">Error</a></h4></section></summary><div class='docblock'>The type returned in the event of a conversion error.</div></details><details class="toggle method-toggle" open><summary><section id="method.try_into" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#798">Source</a><a href="#method.try_into" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryInto.html#tymethod.try_into" class="fn">try_into</a>(self) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;U, &lt;U as <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;T&gt;&gt;::<a class="associatedtype" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#associatedtype.Error" title="type core::convert::TryFrom::Error">Error</a>&gt;</h4></section></summary><div class='docblock'>Performs the conversion.</div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-WithSubscriber-for-T" class="impl"><a href="#impl-WithSubscriber-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; WithSubscriber for T</h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.with_subscriber" class="method trait-impl"><a href="#method.with_subscriber" class="anchor">§</a><h4 class="code-header">fn <a class="fn">with_subscriber</a>&lt;S&gt;(self, subscriber: S) -&gt; WithDispatch&lt;Self&gt;<div class="where">where
S: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;Dispatch&gt;,</div></h4></section></summary><div class='docblock'>Attaches the provided <a href="super::Subscriber"><code>Subscriber</code></a> to this type, returning a
[<code>WithDispatch</code>] wrapper. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.with_current_subscriber" class="method trait-impl"><a href="#method.with_current_subscriber" class="anchor">§</a><h4 class="code-header">fn <a class="fn">with_current_subscriber</a>(self) -&gt; WithDispatch&lt;Self&gt;</h4></section></summary><div class='docblock'>Attaches the current <a href="crate::dispatcher#setting-the-default-subscriber">default</a> <a href="super::Subscriber"><code>Subscriber</code></a> to this type, returning a
[<code>WithDispatch</code>] wrapper. <a>Read more</a></div></details></div></details></div></section></div></main></body></html>

2
crates.js Normal file
View file

@ -0,0 +1,2 @@
window.ALL_CRATES = ["cli","mrc","server"];
//{"start":21,"fragment_lengths":[5,6,9]}

27
flake.lock generated
View file

@ -1,27 +0,0 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1774386573,
"narHash": "sha256-4hAV26quOxdC6iyG7kYaZcM3VOskcPUrdCQd/nx8obc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "46db2e09e1d3f113a13c0d7b81e2f221c63b8ce9",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,25 +0,0 @@
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
outputs = {
self,
nixpkgs,
...
}: let
systems = ["x86_64-linux" "aarch64-linux"];
forEachSystem = nixpkgs.lib.genAttrs systems;
pkgsForEach = nixpkgs.legacyPackages;
in rec {
packages = forEachSystem (system: {
mpvrc = pkgsForEach.${system}.callPackage ./nix/package.nix {};
default = self.packages.${system}.mpvrc;
});
devShells = forEachSystem (system: {
default = pkgsForEach.${system}.callPackage ./nix/shell.nix {};
});
hydraJobs = packages;
};
}

1
help.html Normal file
View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Documentation for Rustdoc"><title>Help</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="./static.files/${f}">`).join(""))</script><link rel="stylesheet" href="./static.files/normalize-9960930a.css"><link rel="stylesheet" href="./static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="./" data-static-root-path="./static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="./static.files/storage-82c7156e.js"></script><script defer src="./static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="./static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="./static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="./static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button><a class="logo-container" href="./index.html"><img class="rust-logo" src="./static.files/rust-logo-9a9549ea.svg" alt=""></a></nav><nav class="sidebar"><div class="sidebar-crate"><a class="logo-container" href="./index.html"><img class="rust-logo" src="./static.files/rust-logo-9a9549ea.svg" alt="logo"></a><h2><a href="./index.html">Rustdoc</a><span class="version">1.87.0</span></h2></div><div class="version">(17067e9ac 2025-05-09)</div><h2 class="location">Help</h2><div class="sidebar-elems"></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1>Rustdoc help</h1><span class="out-of-band"><a id="back" href="javascript:void(0)" onclick="history.back();">Back</a></span></div><noscript><section><p>You need to enable JavaScript to use keyboard commands or search.</p><p>For more information, browse the <a href="https://doc.rust-lang.org/1.87.0/rustdoc/">rustdoc handbook</a>.</p></section></noscript></section></div></main></body></html>

1
index.html Normal file
View file

@ -0,0 +1 @@
<meta http-equiv="refresh" content="0;url=mrc/index.html">

1
mrc/all.html Normal file
View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="List of all items in this crate"><title>List of all items in this crate</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h3><a href="#enums">Crate Items</a></h3><ul class="block"><li><a href="#enums" title="Enums">Enums</a></li><li><a href="#constants" title="Constants">Constants</a></li><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><h1>List of all items</h1><h3 id="enums">Enums</h3><ul class="all-items"><li><a href="enum.MpvCommand.html">MpvCommand</a></li></ul><h3 id="functions">Functions</h3><ul class="all-items"><li><a href="fn.get_property.html">get_property</a></li><li><a href="fn.loadfile.html">loadfile</a></li><li><a href="fn.playlist_clear.html">playlist_clear</a></li><li><a href="fn.playlist_move.html">playlist_move</a></li><li><a href="fn.playlist_next.html">playlist_next</a></li><li><a href="fn.playlist_prev.html">playlist_prev</a></li><li><a href="fn.playlist_remove.html">playlist_remove</a></li><li><a href="fn.quit.html">quit</a></li><li><a href="fn.seek.html">seek</a></li><li><a href="fn.send_ipc_command.html">send_ipc_command</a></li><li><a href="fn.set_property.html">set_property</a></li></ul><h3 id="constants">Constants</h3><ul class="all-items"><li><a href="constant.SOCKET_PATH.html">SOCKET_PATH</a></li></ul></section></div></main></body></html>

View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `SOCKET_PATH` constant in crate `mrc`."><title>SOCKET_PATH in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Constant <span class="constant">SOCKET_PATH</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#49">Source</a> </span></div><pre class="rust item-decl"><code>pub const SOCKET_PATH: &amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a> = &quot;/tmp/mpvsocket&quot;;</code></pre></section></div></main></body></html>

45
mrc/enum.MpvCommand.html Normal file

File diff suppressed because one or more lines are too long

14
mrc/fn.get_property.html Normal file
View file

@ -0,0 +1,14 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `get_property` command to retrieve a property value from MPV."><title>get_property in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">get_<wbr>property</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">get_property</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#320-327">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn get_property(
property: &amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>,
socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;,
) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>get_property</code> command to retrieve a property value from MPV.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>property</code>: The name of the property to retrieve.</li>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

16
mrc/fn.loadfile.html Normal file
View file

@ -0,0 +1,16 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `loadfile` command to load a file into MPV."><title>loadfile in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">loadfile</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">loadfile</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#341-357">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn loadfile(
filename: &amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>,
append: <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.bool.html">bool</a>,
socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;,
) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>loadfile</code> command to load a file into MPV.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>filename</code>: The name of the file to load.</li>
<li><code>append</code>: Whether to append the file to the playlist (<code>true</code>) or replace the current file (<code>false</code>).</li>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `playlist-clear` command to clear the playlist."><title>playlist_clear in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">playlist_<wbr>clear</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">playlist_clear</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#305-307">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn playlist_clear(socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>playlist-clear</code> command to clear the playlist.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

16
mrc/fn.playlist_move.html Normal file
View file

@ -0,0 +1,16 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `playlist-move` command to move a playlist item from one index to another."><title>playlist_move in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">playlist_<wbr>move</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">playlist_move</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#260-271">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn playlist_move(
from_index: <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.usize.html">usize</a>,
to_index: <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.usize.html">usize</a>,
socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;,
) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>playlist-move</code> command to move a playlist item from one index to another.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>from_index</code>: The index of the item to move.</li>
<li><code>to_index</code>: The index to move the item to.</li>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

10
mrc/fn.playlist_next.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `playlist-next` command to move to the next playlist item."><title>playlist_next in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">playlist_<wbr>next</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">playlist_next</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#201-203">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn playlist_next(socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>playlist-next</code> command to move to the next playlist item.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

10
mrc/fn.playlist_prev.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `playlist-prev` command to move to the previous playlist item."><title>playlist_prev in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">playlist_<wbr>prev</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">playlist_prev</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#215-217">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn playlist_prev(socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>playlist-prev</code> command to move to the previous playlist item.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `playlist-remove` command to remove an item from the playlist."><title>playlist_remove in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">playlist_<wbr>remove</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">playlist_remove</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#284-293">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn playlist_remove(
index: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.usize.html">usize</a>&gt;,
socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;,
) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>playlist-remove</code> command to remove an item from the playlist.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>index</code>: The index of the item to remove, or <code>None</code> to remove the current item.</li>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

10
mrc/fn.quit.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `quit` command to terminate MPV."><title>quit in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">quit</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">quit</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#244-246">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn quit(socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>quit</code> command to terminate MPV.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

14
mrc/fn.seek.html Normal file
View file

@ -0,0 +1,14 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `seek` command to seek the media playback by a given number of seconds."><title>seek in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">seek</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">seek</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#230-232">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn seek(
seconds: <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.f64.html">f64</a>,
socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;,
) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>seek</code> command to seek the media playback by a given number of seconds.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>seconds</code>: The number of seconds to seek.</li>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends a generic IPC command to the specified socket and returns the parsed response data."><title>send_ipc_command in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">send_<wbr>ipc_<wbr>command</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">send_ipc_command</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#63-111">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn send_ipc_command(
command: &amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>,
args: &amp;[<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>],
socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;,
) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends a generic IPC command to the specified socket and returns the parsed response data.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>command</code>: The name of the command to send to MPV.</li>
<li><code>args</code>: A slice of <code>Value</code> arguments to include in the command.</li>
<li><code>socket_path</code>: An optional custom path to the MPV IPC socket. If <code>None</code>, the default path is used.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing an <code>Option&lt;Value&gt;</code> with the parsed response data if successful.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or if the response cannot be parsed.</p>
</div></details></section></div></main></body></html>

16
mrc/fn.set_property.html Normal file
View file

@ -0,0 +1,16 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Sends the `set_property` command to MPV to change a property value."><title>set_property in mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">set_<wbr>property</a></h2><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#arguments" title="Arguments">Arguments</a></li><li><a href="#returns" title="Returns">Returns</a></li><li><a href="#errors" title="Errors">Errors</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate mrc</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">mrc</a></div><h1>Function <span class="fn">set_property</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#178-189">Source</a> </span></div><pre class="rust item-decl"><code>pub async fn set_property(
property: &amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>,
value: &amp;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>,
socket_path: <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;&amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>&gt;,
) -&gt; <a class="type" href="https://doc.rust-lang.org/1.87.0/std/io/error/type.Result.html" title="type std::io::error::Result">Result</a>&lt;<a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;<a class="enum" href="https://docs.rs/serde_json/1.0.140/serde_json/value/enum.Value.html" title="enum serde_json::value::Value">Value</a>&gt;&gt;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>Sends the <code>set_property</code> command to MPV to change a property value.</p>
<h2 id="arguments"><a class="doc-anchor" href="#arguments">§</a>Arguments</h2>
<ul>
<li><code>property</code>: The name of the property to set.</li>
<li><code>value</code>: The new value to assign to the property.</li>
<li><code>socket_path</code>: An optional custom socket path.</li>
</ul>
<h2 id="returns"><a class="doc-anchor" href="#returns">§</a>Returns</h2>
<p>A <code>Result</code> containing the response data.</p>
<h2 id="errors"><a class="doc-anchor" href="#errors">§</a>Errors</h2>
<p>Returns an error if the connection to the socket fails or the command execution encounters issues.</p>
</div></details></section></div></main></body></html>

33
mrc/index.html Normal file
View file

@ -0,0 +1,33 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="MRC A library for interacting with the MPV media player using its JSON IPC (Inter-Process Communication) protocol."><title>mrc - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="../crates.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod crate"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../mrc/index.html">mrc</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><ul class="block"><li><a id="all-types" href="all.html">All Items</a></li></ul><section id="rustdoc-toc"><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#features" title="Features">Features</a></li><li><a href="#example-usage" title="Example Usage">Example Usage</a></li><li><a href="#constants" title="Constants">Constants</a><ul><li><a href="#socket_path" title="`SOCKET_PATH`"><code>SOCKET_PATH</code></a></li></ul></li><li><a href="#functions" title="Functions">Functions</a></li></ul><h3><a href="#enums">Crate Items</a></h3><ul class="block"><li><a href="#enums" title="Enums">Enums</a></li><li><a href="#constants-1" title="Constants">Constants</a></li><li><a href="#functions-1" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1>Crate <span>mrc</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/mrc/lib.rs.html#1-357">Source</a> </span></div><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>MRC
A library for interacting with the MPV media player using its JSON IPC (Inter-Process Communication) protocol.</p>
<p>This crate provides a set of utilities to communicate with MPVs IPC socket, enabling you to send commands
and retrieve responses in a structured format.</p>
<h3 id="features"><a class="doc-anchor" href="#features">§</a>Features</h3>
<ul>
<li>Send commands to MPVs IPC socket</li>
<li>Retrieve responses in JSON format</li>
<li>Supports common MPV commands like <code>set_property</code>, <code>seek</code>, and <code>playlist-next</code></li>
<li>Flexible socket path configuration</li>
</ul>
<h3 id="example-usage"><a class="doc-anchor" href="#example-usage">§</a>Example Usage</h3>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>serde_json::json;
<span class="kw">use </span>tokio;
<span class="kw">use </span>mrc::{send_ipc_command, playlist_next, set_property};
<span class="attr">#[tokio::main]
</span><span class="kw">async fn </span>main() {
<span class="kw">let </span>result = playlist_next(<span class="prelude-val">None</span>).<span class="kw">await</span>;
<span class="kw">match </span>result {
<span class="prelude-val">Ok</span>(response) =&gt; <span class="macro">println!</span>(<span class="string">"Playlist moved to next: {:?}"</span>, response),
<span class="prelude-val">Err</span>(err) =&gt; <span class="macro">eprintln!</span>(<span class="string">"Error: {:?}"</span>, err),
}
<span class="kw">let </span>property_result = set_property(<span class="string">"volume"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="number">50</span>), <span class="prelude-val">None</span>).<span class="kw">await</span>;
<span class="kw">match </span>property_result {
<span class="prelude-val">Ok</span>(response) =&gt; <span class="macro">println!</span>(<span class="string">"Volume set: {:?}"</span>, response),
<span class="prelude-val">Err</span>(err) =&gt; <span class="macro">eprintln!</span>(<span class="string">"Error: {:?}"</span>, err),
}
}</code></pre></div>
<h3 id="constants"><a class="doc-anchor" href="#constants">§</a>Constants</h3><h4 id="socket_path"><a class="doc-anchor" href="#socket_path">§</a><code>SOCKET_PATH</code></h4>
<p>Default path for the MPV IPC socket: <code>/tmp/mpvsocket</code></p>
<h3 id="functions"><a class="doc-anchor" href="#functions">§</a>Functions</h3></div></details><h2 id="enums" class="section-header">Enums<a href="#enums" class="anchor">§</a></h2><dl class="item-table"><dt><a class="enum" href="enum.MpvCommand.html" title="enum mrc::MpvCommand">MpvCommand</a></dt><dd>Represents common MPV commands.</dd></dl><h2 id="constants-1" class="section-header">Constants<a href="#constants-1" class="anchor">§</a></h2><dl class="item-table"><dt><a class="constant" href="constant.SOCKET_PATH.html" title="constant mrc::SOCKET_PATH">SOCKET_<wbr>PATH</a></dt></dl><h2 id="functions-1" class="section-header">Functions<a href="#functions-1" class="anchor">§</a></h2><dl class="item-table"><dt><a class="fn" href="fn.get_property.html" title="fn mrc::get_property">get_<wbr>property</a></dt><dd>Sends the <code>get_property</code> command to retrieve a property value from MPV.</dd><dt><a class="fn" href="fn.loadfile.html" title="fn mrc::loadfile">loadfile</a></dt><dd>Sends the <code>loadfile</code> command to load a file into MPV.</dd><dt><a class="fn" href="fn.playlist_clear.html" title="fn mrc::playlist_clear">playlist_<wbr>clear</a></dt><dd>Sends the <code>playlist-clear</code> command to clear the playlist.</dd><dt><a class="fn" href="fn.playlist_move.html" title="fn mrc::playlist_move">playlist_<wbr>move</a></dt><dd>Sends the <code>playlist-move</code> command to move a playlist item from one index to another.</dd><dt><a class="fn" href="fn.playlist_next.html" title="fn mrc::playlist_next">playlist_<wbr>next</a></dt><dd>Sends the <code>playlist-next</code> command to move to the next playlist item.</dd><dt><a class="fn" href="fn.playlist_prev.html" title="fn mrc::playlist_prev">playlist_<wbr>prev</a></dt><dd>Sends the <code>playlist-prev</code> command to move to the previous playlist item.</dd><dt><a class="fn" href="fn.playlist_remove.html" title="fn mrc::playlist_remove">playlist_<wbr>remove</a></dt><dd>Sends the <code>playlist-remove</code> command to remove an item from the playlist.</dd><dt><a class="fn" href="fn.quit.html" title="fn mrc::quit">quit</a></dt><dd>Sends the <code>quit</code> command to terminate MPV.</dd><dt><a class="fn" href="fn.seek.html" title="fn mrc::seek">seek</a></dt><dd>Sends the <code>seek</code> command to seek the media playback by a given number of seconds.</dd><dt><a class="fn" href="fn.send_ipc_command.html" title="fn mrc::send_ipc_command">send_<wbr>ipc_<wbr>command</a></dt><dd>Sends a generic IPC command to the specified socket and returns the parsed response data.</dd><dt><a class="fn" href="fn.set_property.html" title="fn mrc::set_property">set_<wbr>property</a></dt><dd>Sends the <code>set_property</code> command to MPV to change a property value.</dd></dl></section></div></main></body></html>

1
mrc/sidebar-items.js Normal file
View file

@ -0,0 +1 @@
window.SIDEBAR_ITEMS = {"constant":["SOCKET_PATH"],"enum":["MpvCommand"],"fn":["get_property","loadfile","playlist_clear","playlist_move","playlist_next","playlist_prev","playlist_remove","quit","seek","send_ipc_command","set_property"]};

View file

@ -1,43 +0,0 @@
{
lib,
rustPlatform,
pkg-config,
openssl,
}: let
fs = lib.fileset;
s = ../.;
in
rustPlatform.buildRustPackage (finalAttrs: {
pname = "mpvrc";
version = (lib.importTOML (s + /Cargo.toml)).package.version;
src = fs.toSource {
root = s;
fileset = fs.unions [
(fs.fileFilter (file: builtins.any file.hasExt ["rs"]) (s + /src))
(s + /Cargo.lock)
(s + /Cargo.toml)
];
};
strictDeps = true;
enableParallelBuilding = true;
nativeBuildInputs = [
pkg-config
];
buildInputs = [
openssl
];
cargoLock.lockFile = "${finalAttrs.src}/Cargo.lock";
meta = {
description = "IPC wrapper & command-line controller for MPV, the video player";
homePage = "https://github.com/notashelf/mpvrc";
mainProgram = "mpvrc";
license = lib.licenses.mpl20;
maintainers = [lib.maintainers.NotAShelf];
};
})

View file

@ -1,27 +0,0 @@
{
mkShell,
rust-analyzer,
rustfmt,
clippy,
cargo,
taplo,
openssl,
pkg-config,
rustc,
}:
mkShell {
name = "mpvrc";
packages = [
cargo
rustc
rust-analyzer
clippy
(rustfmt.override {asNightly = true;})
taplo
# For TLS and friends
openssl
pkg-config
];
}

4
search-index.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
searchState.loadedDescShard("cli", 0, "Add files to the playlist\nClear the entire playlist\nEnter interactive mode to send commands to MPV IPC\nList all the items in the playlist\nMove an item in the playlist from one index to another\nSkip to the next item in the playlist\nPause the currently playing media\nPlay media at the specified index in the playlist\nSkip to the previous item in the playlist\nFetch properties of the current playback or playlist\nRemove an item from the playlist\nReplace the current playlist with new files\nSeek to a specific position in the currently playing media\nStop the playback and quit MPV\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls <code>U::from(self)</code>.\nCalls <code>U::from(self)</code>.\nThe filenames of the files to add\nThe filenames of the files to replace the playlist with\nThe index of the media to play\nThe index of the item to remove (optional)\nThe index of the item to move\nThe index to move the item to\nThe properties to fetch\nThe number of seconds to seek to")

View file

@ -0,0 +1 @@
searchState.loadedDescShard("mrc", 0, "MRC A library for interacting with the MPV media player …\nRetrieves the value of a property in MPV.\nLoads a file into MPV.\nRepresents common MPV commands.\nClears all items from the playlist.\nMoves an item in the playlist from one index to another.\nMoves to the next item in the playlist.\nMoves to the previous item in the playlist.\nRemoves an item from the playlist.\nQuits the MPV application.\nSeeks to a specific time in the current media.\nSets a property to a specified value in MPV.\nConverts MPV commands to their string equivalents.\nReturns the argument unchanged.\nSends the <code>get_property</code> command to retrieve a property …\nCalls <code>U::from(self)</code>.\nSends the <code>loadfile</code> command to load a file into MPV.\nSends the <code>playlist-clear</code> command to clear the playlist.\nSends the <code>playlist-move</code> command to move a playlist item …\nSends the <code>playlist-next</code> command to move to the next …\nSends the <code>playlist-prev</code> command to move to the previous …\nSends the <code>playlist-remove</code> command to remove an item from …\nSends the <code>quit</code> command to terminate MPV.\nSends the <code>seek</code> command to seek the media playback by a …\nSends a generic IPC command to the specified socket and …\nSends the <code>set_property</code> command to MPV to change a property …")

View file

@ -0,0 +1 @@
searchState.loadedDescShard("server", 0, "The IP address and port to bind the server to\nReturns the argument unchanged.\nCalls <code>U::from(self)</code>.\nPath to MPV IPC socket")

1
server/all.html Normal file
View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="List of all items in this crate"><title>List of all items in this crate</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../server/index.html">server</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h3><a href="#structs">Crate Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><h1>List of all items</h1><h3 id="structs">Structs</h3><ul class="all-items"><li><a href="struct.Config.html">Config</a></li></ul><h3 id="functions">Functions</h3><ul class="all-items"><li><a href="fn.create_tls_acceptor.html">create_tls_acceptor</a></li><li><a href="fn.handle_connection.html">handle_connection</a></li><li><a href="fn.main.html">main</a></li><li><a href="fn.process_command.html">process_command</a></li></ul></section></div></main></body></html>

View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `create_tls_acceptor` fn in crate `server`."><title>create_tls_acceptor in server - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../server/index.html">server</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">server</a></div><h1>Function <span class="fn">create_tls_acceptor</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/server/server.rs.html#169-182">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) fn create_tls_acceptor() -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="struct" href="https://docs.rs/tokio-native-tls/0.3.0/tokio_native_tls/struct.TlsAcceptor.html" title="struct tokio_native_tls::TlsAcceptor">TlsAcceptor</a>, <a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/boxed/struct.Box.html" title="struct alloc::boxed::Box">Box</a>&lt;dyn <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/error/trait.Error.html" title="trait core::error::Error">Error</a> + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Send.html" title="trait core::marker::Send">Send</a> + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sync.html" title="trait core::marker::Sync">Sync</a>&gt;&gt;</code></pre></section></div></main></body></html>

View file

@ -0,0 +1,4 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `handle_connection` fn in crate `server`."><title>handle_connection in server - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../server/index.html">server</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">server</a></div><h1>Function <span class="fn">handle_connection</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/server/server.rs.html#26-88">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) async fn handle_connection(
stream: TcpStream,
acceptor: <a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/sync/struct.Arc.html" title="struct alloc::sync::Arc">Arc</a>&lt;<a class="struct" href="https://docs.rs/tokio-native-tls/0.3.0/tokio_native_tls/struct.TlsAcceptor.html" title="struct tokio_native_tls::TlsAcceptor">TlsAcceptor</a>&gt;,
) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.unit.html">()</a>, <a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/boxed/struct.Box.html" title="struct alloc::boxed::Box">Box</a>&lt;dyn <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/error/trait.Error.html" title="trait core::error::Error">Error</a> + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Send.html" title="trait core::marker::Send">Send</a> + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sync.html" title="trait core::marker::Sync">Sync</a>&gt;&gt;</code></pre></section></div></main></body></html>

1
server/fn.main.html Normal file
View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `main` fn in crate `server`."><title>main in server - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../server/index.html">server</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">server</a></div><h1>Function <span class="fn">main</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/server/server.rs.html#185-217">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) fn main() -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.unit.html">()</a>, <a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/boxed/struct.Box.html" title="struct alloc::boxed::Box">Box</a>&lt;dyn <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/error/trait.Error.html" title="trait core::error::Error">Error</a> + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Send.html" title="trait core::marker::Send">Send</a> + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sync.html" title="trait core::marker::Sync">Sync</a>&gt;&gt;</code></pre></section></div></main></body></html>

View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `process_command` fn in crate `server`."><title>process_command in server - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc fn"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../server/index.html">server</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">server</a></div><h1>Function <span class="fn">process_command</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/server/server.rs.html#90-167">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) async fn process_command(command: &amp;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.str.html">str</a>) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>, <a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>&gt;</code></pre></section></div></main></body></html>

1
server/index.html Normal file
View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `server` crate."><title>server - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="../crates.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod crate"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../server/index.html">server</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><ul class="block"><li><a id="all-types" href="all.html">All Items</a></li></ul><section id="rustdoc-toc"><h3><a href="#structs">Crate Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#functions" title="Functions">Functions</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1>Crate <span>server</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/server/server.rs.html#1-217">Source</a> </span></div><h2 id="structs" class="section-header">Structs<a href="#structs" class="anchor">§</a></h2><dl class="item-table"><dt><a class="struct" href="struct.Config.html" title="struct server::Config">Config</a><span title="Restricted Visibility">&nbsp;🔒</span> </dt></dl><h2 id="functions" class="section-header">Functions<a href="#functions" class="anchor">§</a></h2><dl class="item-table"><dt><a class="fn" href="fn.create_tls_acceptor.html" title="fn server::create_tls_acceptor">create_<wbr>tls_<wbr>acceptor</a><span title="Restricted Visibility">&nbsp;🔒</span> </dt><dt><a class="fn" href="fn.handle_connection.html" title="fn server::handle_connection">handle_<wbr>connection</a><span title="Restricted Visibility">&nbsp;🔒</span> </dt><dt><a class="fn" href="fn.main.html" title="fn server::main">main</a><span title="Restricted Visibility">&nbsp;🔒</span> </dt><dt><a class="fn" href="fn.process_command.html" title="fn server::process_command">process_<wbr>command</a><span title="Restricted Visibility">&nbsp;🔒</span> </dt></dl></section></div></main></body></html>

1
server/sidebar-items.js Normal file
View file

@ -0,0 +1 @@
window.SIDEBAR_ITEMS = {"fn":["create_tls_acceptor","handle_connection","main","process_command"],"struct":["Config"]};

39
server/struct.Config.html Normal file
View file

@ -0,0 +1,39 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="API documentation for the Rust `Config` struct in crate `server`."><title>Config in server - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../static.files/storage-82c7156e.js"></script><script defer src="sidebar-items.js"></script><script defer src="../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc struct"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../server/index.html">server</a><span class="version">0.1.0</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h2 class="location"><a href="#">Config</a></h2><h3><a href="#fields">Fields</a></h3><ul class="block structfield"><li><a href="#structfield.bind" title="bind">bind</a></li><li><a href="#structfield.socket" title="socket">socket</a></li></ul><h3><a href="#trait-implementations">Trait Implementations</a></h3><ul class="block trait-implementation"><li><a href="#impl-Args-for-Config" title="Args">Args</a></li><li><a href="#impl-CommandFactory-for-Config" title="CommandFactory">CommandFactory</a></li><li><a href="#impl-FromArgMatches-for-Config" title="FromArgMatches">FromArgMatches</a></li><li><a href="#impl-Parser-for-Config" title="Parser">Parser</a></li></ul><h3><a href="#synthetic-implementations">Auto Trait Implementations</a></h3><ul class="block synthetic-implementation"><li><a href="#impl-Freeze-for-Config" title="Freeze">Freeze</a></li><li><a href="#impl-RefUnwindSafe-for-Config" title="RefUnwindSafe">RefUnwindSafe</a></li><li><a href="#impl-Send-for-Config" title="Send">Send</a></li><li><a href="#impl-Sync-for-Config" title="Sync">Sync</a></li><li><a href="#impl-Unpin-for-Config" title="Unpin">Unpin</a></li><li><a href="#impl-UnwindSafe-for-Config" title="UnwindSafe">UnwindSafe</a></li></ul><h3><a href="#blanket-implementations">Blanket Implementations</a></h3><ul class="block blanket-implementation"><li><a href="#impl-Any-for-T" title="Any">Any</a></li><li><a href="#impl-Borrow%3CT%3E-for-T" title="Borrow&#60;T&#62;">Borrow&#60;T&#62;</a></li><li><a href="#impl-BorrowMut%3CT%3E-for-T" title="BorrowMut&#60;T&#62;">BorrowMut&#60;T&#62;</a></li><li><a href="#impl-From%3CT%3E-for-T" title="From&#60;T&#62;">From&#60;T&#62;</a></li><li><a href="#impl-Instrument-for-T" title="Instrument">Instrument</a></li><li><a href="#impl-Into%3CU%3E-for-T" title="Into&#60;U&#62;">Into&#60;U&#62;</a></li><li><a href="#impl-TryFrom%3CU%3E-for-T" title="TryFrom&#60;U&#62;">TryFrom&#60;U&#62;</a></li><li><a href="#impl-TryInto%3CU%3E-for-T" title="TryInto&#60;U&#62;">TryInto&#60;U&#62;</a></li><li><a href="#impl-WithSubscriber-for-T" title="WithSubscriber">WithSubscriber</a></li></ul></section><div id="rustdoc-modnav"><h2 class="in-crate"><a href="index.html">In crate server</a></h2></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="index.html">server</a></div><h1>Struct <span class="struct">Config</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/server/server.rs.html#16-24">Source</a> </span></div><pre class="rust item-decl"><code>pub(crate) struct Config {
pub(crate) bind: <a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
pub(crate) socket: <a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>,
}</code></pre><h2 id="fields" class="fields section-header">Fields<a href="#fields" class="anchor">§</a></h2><span id="structfield.bind" class="structfield section-header"><a href="#structfield.bind" class="anchor field">§</a><code>bind: <a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/string/struct.String.html" title="struct alloc::string::String">String</a></code></span><div class="docblock"><p>The IP address and port to bind the server to</p>
</div><span id="structfield.socket" class="structfield section-header"><a href="#structfield.socket" class="anchor field">§</a><code>socket: <a class="struct" href="https://doc.rust-lang.org/1.87.0/alloc/string/struct.String.html" title="struct alloc::string::String">String</a></code></span><div class="docblock"><p>Path to MPV IPC socket</p>
</div><h2 id="trait-implementations" class="section-header">Trait Implementations<a href="#trait-implementations" class="anchor">§</a></h2><div id="trait-implementations-list"><details class="toggle implementors-toggle" open><summary><section id="impl-Args-for-Config" class="impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#impl-Args-for-Config" class="anchor">§</a><h3 class="code-header">impl Args for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.group_id" class="method trait-impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#method.group_id" class="anchor">§</a><h4 class="code-header">fn <a class="fn">group_id</a>() -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/option/enum.Option.html" title="enum core::option::Option">Option</a>&lt;Id&gt;</h4></section></summary><div class='docblock'>Report the [<code>ArgGroup::id</code>][crate::ArgGroup::id] for this set of arguments</div></details><details class="toggle method-toggle" open><summary><section id="method.augment_args" class="method trait-impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#method.augment_args" class="anchor">§</a><h4 class="code-header">fn <a class="fn">augment_args</a>&lt;'b&gt;(__clap_app: Command) -&gt; Command</h4></section></summary><div class='docblock'>Append to [<code>Command</code>] so it can instantiate <code>Self</code> via
[<code>FromArgMatches::from_arg_matches_mut</code>] <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.augment_args_for_update" class="method trait-impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#method.augment_args_for_update" class="anchor">§</a><h4 class="code-header">fn <a class="fn">augment_args_for_update</a>&lt;'b&gt;(__clap_app: Command) -&gt; Command</h4></section></summary><div class='docblock'>Append to [<code>Command</code>] so it can instantiate <code>self</code> via
[<code>FromArgMatches::update_from_arg_matches_mut</code>] <a>Read more</a></div></details></div></details><details class="toggle implementors-toggle" open><summary><section id="impl-CommandFactory-for-Config" class="impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#impl-CommandFactory-for-Config" class="anchor">§</a><h3 class="code-header">impl CommandFactory for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.command" class="method trait-impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#method.command" class="anchor">§</a><h4 class="code-header">fn <a class="fn">command</a>&lt;'b&gt;() -&gt; Command</h4></section></summary><div class='docblock'>Build a [<code>Command</code>] that can instantiate <code>Self</code>. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.command_for_update" class="method trait-impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#method.command_for_update" class="anchor">§</a><h4 class="code-header">fn <a class="fn">command_for_update</a>&lt;'b&gt;() -&gt; Command</h4></section></summary><div class='docblock'>Build a [<code>Command</code>] that can update <code>self</code>. <a>Read more</a></div></details></div></details><details class="toggle implementors-toggle" open><summary><section id="impl-FromArgMatches-for-Config" class="impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#impl-FromArgMatches-for-Config" class="anchor">§</a><h3 class="code-header">impl FromArgMatches for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.from_arg_matches" class="method trait-impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#method.from_arg_matches" class="anchor">§</a><h4 class="code-header">fn <a class="fn">from_arg_matches</a>(__clap_arg_matches: &amp;ArgMatches) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;Self, Error&gt;</h4></section></summary><div class='docblock'>Instantiate <code>Self</code> from [<code>ArgMatches</code>], parsing the arguments as needed. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.from_arg_matches_mut" class="method trait-impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#method.from_arg_matches_mut" class="anchor">§</a><h4 class="code-header">fn <a class="fn">from_arg_matches_mut</a>(
__clap_arg_matches: &amp;mut ArgMatches,
) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;Self, Error&gt;</h4></section></summary><div class='docblock'>Instantiate <code>Self</code> from [<code>ArgMatches</code>], parsing the arguments as needed. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.update_from_arg_matches" class="method trait-impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#method.update_from_arg_matches" class="anchor">§</a><h4 class="code-header">fn <a class="fn">update_from_arg_matches</a>(
&amp;mut self,
__clap_arg_matches: &amp;ArgMatches,
) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.unit.html">()</a>, Error&gt;</h4></section></summary><div class='docblock'>Assign values from <code>ArgMatches</code> to <code>self</code>.</div></details><details class="toggle method-toggle" open><summary><section id="method.update_from_arg_matches_mut" class="method trait-impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#method.update_from_arg_matches_mut" class="anchor">§</a><h4 class="code-header">fn <a class="fn">update_from_arg_matches_mut</a>(
&amp;mut self,
__clap_arg_matches: &amp;mut ArgMatches,
) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.unit.html">()</a>, Error&gt;</h4></section></summary><div class='docblock'>Assign values from <code>ArgMatches</code> to <code>self</code>.</div></details></div></details><details class="toggle implementors-toggle" open><summary><section id="impl-Parser-for-Config" class="impl"><a class="src rightside" href="../src/server/server.rs.html#14">Source</a><a href="#impl-Parser-for-Config" class="anchor">§</a><h3 class="code-header">impl Parser for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.parse" class="method trait-impl"><a href="#method.parse" class="anchor">§</a><h4 class="code-header">fn <a class="fn">parse</a>() -&gt; Self</h4></section></summary><div class='docblock'>Parse from <code>std::env::args_os()</code>, [exit][Error::exit] on error.</div></details><details class="toggle method-toggle" open><summary><section id="method.try_parse" class="method trait-impl"><a href="#method.try_parse" class="anchor">§</a><h4 class="code-header">fn <a class="fn">try_parse</a>() -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;Self, Error&gt;</h4></section></summary><div class='docblock'>Parse from <code>std::env::args_os()</code>, return Err on error.</div></details><details class="toggle method-toggle" open><summary><section id="method.parse_from" class="method trait-impl"><a href="#method.parse_from" class="anchor">§</a><h4 class="code-header">fn <a class="fn">parse_from</a>&lt;I, T&gt;(itr: I) -&gt; Self<div class="where">where
I: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/iter/traits/collect/trait.IntoIterator.html" title="trait core::iter::traits::collect::IntoIterator">IntoIterator</a>&lt;Item = T&gt;,
T: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;<a class="struct" href="https://doc.rust-lang.org/1.87.0/std/ffi/os_str/struct.OsString.html" title="struct std::ffi::os_str::OsString">OsString</a>&gt; + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</div></h4></section></summary><div class='docblock'>Parse from iterator, [exit][Error::exit] on error.</div></details><details class="toggle method-toggle" open><summary><section id="method.try_parse_from" class="method trait-impl"><a href="#method.try_parse_from" class="anchor">§</a><h4 class="code-header">fn <a class="fn">try_parse_from</a>&lt;I, T&gt;(itr: I) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;Self, Error&gt;<div class="where">where
I: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/iter/traits/collect/trait.IntoIterator.html" title="trait core::iter::traits::collect::IntoIterator">IntoIterator</a>&lt;Item = T&gt;,
T: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;<a class="struct" href="https://doc.rust-lang.org/1.87.0/std/ffi/os_str/struct.OsString.html" title="struct std::ffi::os_str::OsString">OsString</a>&gt; + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</div></h4></section></summary><div class='docblock'>Parse from iterator, return Err on error.</div></details><details class="toggle method-toggle" open><summary><section id="method.update_from" class="method trait-impl"><a href="#method.update_from" class="anchor">§</a><h4 class="code-header">fn <a class="fn">update_from</a>&lt;I, T&gt;(&amp;mut self, itr: I)<div class="where">where
I: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/iter/traits/collect/trait.IntoIterator.html" title="trait core::iter::traits::collect::IntoIterator">IntoIterator</a>&lt;Item = T&gt;,
T: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;<a class="struct" href="https://doc.rust-lang.org/1.87.0/std/ffi/os_str/struct.OsString.html" title="struct std::ffi::os_str::OsString">OsString</a>&gt; + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</div></h4></section></summary><div class='docblock'>Update from iterator, [exit][Error::exit] on error. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.try_update_from" class="method trait-impl"><a href="#method.try_update_from" class="anchor">§</a><h4 class="code-header">fn <a class="fn">try_update_from</a>&lt;I, T&gt;(&amp;mut self, itr: I) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;<a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.unit.html">()</a>, Error&gt;<div class="where">where
I: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/iter/traits/collect/trait.IntoIterator.html" title="trait core::iter::traits::collect::IntoIterator">IntoIterator</a>&lt;Item = T&gt;,
T: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;<a class="struct" href="https://doc.rust-lang.org/1.87.0/std/ffi/os_str/struct.OsString.html" title="struct std::ffi::os_str::OsString">OsString</a>&gt; + <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</div></h4></section></summary><div class='docblock'>Update from iterator, return Err on error.</div></details></div></details></div><h2 id="synthetic-implementations" class="section-header">Auto Trait Implementations<a href="#synthetic-implementations" class="anchor">§</a></h2><div id="synthetic-implementations-list"><section id="impl-Freeze-for-Config" class="impl"><a href="#impl-Freeze-for-Config" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Freeze.html" title="trait core::marker::Freeze">Freeze</a> for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section><section id="impl-RefUnwindSafe-for-Config" class="impl"><a href="#impl-RefUnwindSafe-for-Config" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/panic/unwind_safe/trait.RefUnwindSafe.html" title="trait core::panic::unwind_safe::RefUnwindSafe">RefUnwindSafe</a> for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section><section id="impl-Send-for-Config" class="impl"><a href="#impl-Send-for-Config" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Send.html" title="trait core::marker::Send">Send</a> for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section><section id="impl-Sync-for-Config" class="impl"><a href="#impl-Sync-for-Config" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sync.html" title="trait core::marker::Sync">Sync</a> for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section><section id="impl-Unpin-for-Config" class="impl"><a href="#impl-Unpin-for-Config" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Unpin.html" title="trait core::marker::Unpin">Unpin</a> for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section><section id="impl-UnwindSafe-for-Config" class="impl"><a href="#impl-UnwindSafe-for-Config" class="anchor">§</a><h3 class="code-header">impl <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/panic/unwind_safe/trait.UnwindSafe.html" title="trait core::panic::unwind_safe::UnwindSafe">UnwindSafe</a> for <a class="struct" href="struct.Config.html" title="struct server::Config">Config</a></h3></section></div><h2 id="blanket-implementations" class="section-header">Blanket Implementations<a href="#blanket-implementations" class="anchor">§</a></h2><div id="blanket-implementations-list"><details class="toggle implementors-toggle"><summary><section id="impl-Any-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/any.rs.html#138">Source</a><a href="#impl-Any-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/any/trait.Any.html" title="trait core::any::Any">Any</a> for T<div class="where">where
T: 'static + ?<a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,</div></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.type_id" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/any.rs.html#139">Source</a><a href="#method.type_id" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/any/trait.Any.html#tymethod.type_id" class="fn">type_id</a>(&amp;self) -&gt; <a class="struct" href="https://doc.rust-lang.org/1.87.0/core/any/struct.TypeId.html" title="struct core::any::TypeId">TypeId</a></h4></section></summary><div class='docblock'>Gets the <code>TypeId</code> of <code>self</code>. <a href="https://doc.rust-lang.org/1.87.0/core/any/trait.Any.html#tymethod.type_id">Read more</a></div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-Borrow%3CT%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/borrow.rs.html#209">Source</a><a href="#impl-Borrow%3CT%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.Borrow.html" title="trait core::borrow::Borrow">Borrow</a>&lt;T&gt; for T<div class="where">where
T: ?<a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,</div></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.borrow" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/borrow.rs.html#211">Source</a><a href="#method.borrow" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.Borrow.html#tymethod.borrow" class="fn">borrow</a>(&amp;self) -&gt; <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.reference.html">&amp;T</a></h4></section></summary><div class='docblock'>Immutably borrows from an owned value. <a href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.Borrow.html#tymethod.borrow">Read more</a></div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-BorrowMut%3CT%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/borrow.rs.html#217">Source</a><a href="#impl-BorrowMut%3CT%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.BorrowMut.html" title="trait core::borrow::BorrowMut">BorrowMut</a>&lt;T&gt; for T<div class="where">where
T: ?<a class="trait" href="https://doc.rust-lang.org/1.87.0/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,</div></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.borrow_mut" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/borrow.rs.html#218">Source</a><a href="#method.borrow_mut" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.BorrowMut.html#tymethod.borrow_mut" class="fn">borrow_mut</a>(&amp;mut self) -&gt; <a class="primitive" href="https://doc.rust-lang.org/1.87.0/std/primitive.reference.html">&amp;mut T</a></h4></section></summary><div class='docblock'>Mutably borrows from an owned value. <a href="https://doc.rust-lang.org/1.87.0/core/borrow/trait.BorrowMut.html#tymethod.borrow_mut">Read more</a></div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-From%3CT%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#767">Source</a><a href="#impl-From%3CT%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.From.html" title="trait core::convert::From">From</a>&lt;T&gt; for T</h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.from" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#770">Source</a><a href="#method.from" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.From.html#tymethod.from" class="fn">from</a>(t: T) -&gt; T</h4></section></summary><div class="docblock"><p>Returns the argument unchanged.</p>
</div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-Instrument-for-T" class="impl"><a href="#impl-Instrument-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; Instrument for T</h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.instrument" class="method trait-impl"><a href="#method.instrument" class="anchor">§</a><h4 class="code-header">fn <a class="fn">instrument</a>(self, span: Span) -&gt; Instrumented&lt;Self&gt;</h4></section></summary><div class='docblock'>Instruments this type with the provided [<code>Span</code>], returning an
<code>Instrumented</code> wrapper. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.in_current_span" class="method trait-impl"><a href="#method.in_current_span" class="anchor">§</a><h4 class="code-header">fn <a class="fn">in_current_span</a>(self) -&gt; Instrumented&lt;Self&gt;</h4></section></summary><div class='docblock'>Instruments this type with the <a href="super::Span::current()">current</a> <a href="crate::Span"><code>Span</code></a>, returning an
<code>Instrumented</code> wrapper. <a>Read more</a></div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-Into%3CU%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#750-752">Source</a><a href="#impl-Into%3CU%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T, U&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;U&gt; for T<div class="where">where
U: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.From.html" title="trait core::convert::From">From</a>&lt;T&gt;,</div></h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.into" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#760">Source</a><a href="#method.into" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html#tymethod.into" class="fn">into</a>(self) -&gt; U</h4></section></summary><div class="docblock"><p>Calls <code>U::from(self)</code>.</p>
<p>That is, this conversion is whatever the implementation of
<code><a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.From.html" title="trait core::convert::From">From</a>&lt;T&gt; for U</code> chooses to do.</p>
</div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-TryFrom%3CU%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#806-808">Source</a><a href="#impl-TryFrom%3CU%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T, U&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;U&gt; for T<div class="where">where
U: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;T&gt;,</div></h3></section></summary><div class="impl-items"><details class="toggle" open><summary><section id="associatedtype.Error-1" class="associatedtype trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#810">Source</a><a href="#associatedtype.Error-1" class="anchor">§</a><h4 class="code-header">type <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#associatedtype.Error" class="associatedtype">Error</a> = <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/convert/enum.Infallible.html" title="enum core::convert::Infallible">Infallible</a></h4></section></summary><div class='docblock'>The type returned in the event of a conversion error.</div></details><details class="toggle method-toggle" open><summary><section id="method.try_from" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#813">Source</a><a href="#method.try_from" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#tymethod.try_from" class="fn">try_from</a>(value: U) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;T, &lt;T as <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;U&gt;&gt;::<a class="associatedtype" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#associatedtype.Error" title="type core::convert::TryFrom::Error">Error</a>&gt;</h4></section></summary><div class='docblock'>Performs the conversion.</div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-TryInto%3CU%3E-for-T" class="impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#791-793">Source</a><a href="#impl-TryInto%3CU%3E-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T, U&gt; <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryInto.html" title="trait core::convert::TryInto">TryInto</a>&lt;U&gt; for T<div class="where">where
U: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;T&gt;,</div></h3></section></summary><div class="impl-items"><details class="toggle" open><summary><section id="associatedtype.Error" class="associatedtype trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#795">Source</a><a href="#associatedtype.Error" class="anchor">§</a><h4 class="code-header">type <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryInto.html#associatedtype.Error" class="associatedtype">Error</a> = &lt;U as <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;T&gt;&gt;::<a class="associatedtype" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#associatedtype.Error" title="type core::convert::TryFrom::Error">Error</a></h4></section></summary><div class='docblock'>The type returned in the event of a conversion error.</div></details><details class="toggle method-toggle" open><summary><section id="method.try_into" class="method trait-impl"><a class="src rightside" href="https://doc.rust-lang.org/1.87.0/src/core/convert/mod.rs.html#798">Source</a><a href="#method.try_into" class="anchor">§</a><h4 class="code-header">fn <a href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryInto.html#tymethod.try_into" class="fn">try_into</a>(self) -&gt; <a class="enum" href="https://doc.rust-lang.org/1.87.0/core/result/enum.Result.html" title="enum core::result::Result">Result</a>&lt;U, &lt;U as <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html" title="trait core::convert::TryFrom">TryFrom</a>&lt;T&gt;&gt;::<a class="associatedtype" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.TryFrom.html#associatedtype.Error" title="type core::convert::TryFrom::Error">Error</a>&gt;</h4></section></summary><div class='docblock'>Performs the conversion.</div></details></div></details><details class="toggle implementors-toggle"><summary><section id="impl-WithSubscriber-for-T" class="impl"><a href="#impl-WithSubscriber-for-T" class="anchor">§</a><h3 class="code-header">impl&lt;T&gt; WithSubscriber for T</h3></section></summary><div class="impl-items"><details class="toggle method-toggle" open><summary><section id="method.with_subscriber" class="method trait-impl"><a href="#method.with_subscriber" class="anchor">§</a><h4 class="code-header">fn <a class="fn">with_subscriber</a>&lt;S&gt;(self, subscriber: S) -&gt; WithDispatch&lt;Self&gt;<div class="where">where
S: <a class="trait" href="https://doc.rust-lang.org/1.87.0/core/convert/trait.Into.html" title="trait core::convert::Into">Into</a>&lt;Dispatch&gt;,</div></h4></section></summary><div class='docblock'>Attaches the provided <a href="super::Subscriber"><code>Subscriber</code></a> to this type, returning a
[<code>WithDispatch</code>] wrapper. <a>Read more</a></div></details><details class="toggle method-toggle" open><summary><section id="method.with_current_subscriber" class="method trait-impl"><a href="#method.with_current_subscriber" class="anchor">§</a><h4 class="code-header">fn <a class="fn">with_current_subscriber</a>(self) -&gt; WithDispatch&lt;Self&gt;</h4></section></summary><div class='docblock'>Attaches the current <a href="crate::dispatcher#setting-the-default-subscriber">default</a> <a href="super::Subscriber"><code>Subscriber</code></a> to this type, returning a
[<code>WithDispatch</code>] wrapper. <a>Read more</a></div></details></div></details></div></section></div></main></body></html>

1
settings.html Normal file
View file

@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Settings of Rustdoc"><title>Settings</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="./static.files/${f}">`).join(""))</script><link rel="stylesheet" href="./static.files/normalize-9960930a.css"><link rel="stylesheet" href="./static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="./" data-static-root-path="./static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="./static.files/storage-82c7156e.js"></script><script defer src="./static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="./static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="./static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="./static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button><a class="logo-container" href="./index.html"><img class="rust-logo" src="./static.files/rust-logo-9a9549ea.svg" alt=""></a></nav><nav class="sidebar"><div class="sidebar-crate"><a class="logo-container" href="./index.html"><img class="rust-logo" src="./static.files/rust-logo-9a9549ea.svg" alt="logo"></a><h2><a href="./index.html">Rustdoc</a><span class="version">1.87.0</span></h2></div><div class="version">(17067e9ac 2025-05-09)</div><h2 class="location">Settings</h2><div class="sidebar-elems"></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1>Rustdoc settings</h1><span class="out-of-band"><a id="back" href="javascript:void(0)" onclick="history.back();">Back</a></span></div><noscript><section>You need to enable JavaScript be able to update your settings.</section></noscript><script defer src="./static.files/settings-d72f25bb.js"></script></section></div></main></body></html>

3
src-files.js Normal file
View file

@ -0,0 +1,3 @@
var srcIndex = new Map(JSON.parse('[["cli",["",[],["cli.rs"]]],["mrc",["",[],["lib.rs"]]],["server",["",[],["server.rs"]]]]'));
createSrcSidebar();
//{"start":36,"fragment_lengths":[26,27,33]}

View file

@ -1,280 +0,0 @@
use std::{
io::{self, Write},
path::PathBuf,
};
use clap::{CommandFactory, Parser, Subcommand, ValueEnum};
use mpvrc::{MrcError, Result, SOCKET_PATH, commands::Commands, interactive::InteractiveMode};
use tracing::{debug, error};
#[derive(Parser)]
#[command(author, version, about)]
struct Cli {
#[arg(short, long, global = true, help = "Path to MPV socket")]
socket: Option<String>,
#[arg(short, long, global = true, help = "Skip confirmation prompts")]
yes: bool,
#[arg(short, long, global = true, help = "Enable debug output")]
debug: bool,
#[command(subcommand)]
command: CommandOptions,
}
#[derive(Subcommand)]
enum CommandOptions {
/// Play media at the specified index in the playlist
Play {
/// The index of the media to play
index: Option<usize>,
},
/// Pause the currently playing media
Pause,
/// Stop the playback and quit MPV
Stop,
/// Skip to the next item in the playlist
Next,
/// Skip to the previous item in the playlist
Prev,
/// Seek to a specific position in the currently playing media
Seek {
/// The number of seconds to seek to
seconds: i32,
},
/// Move an item in the playlist from one index to another
Move {
/// The index of the item to move
index1: usize,
/// The index to move the item to
index2: usize,
},
/// Remove an item from the playlist
///
/// If invoked while playlist has no entries, or if the only entry
/// is the active video, then this will exit MPV.
Remove {
/// The index of the item to remove (optional)
index: Option<usize>,
},
/// Clear the entire playlist
Clear,
/// List all the items in the playlist
List,
/// Add files to the playlist
///
/// Needs at least one file to be passed.
Add {
/// The filenames of the files to add
filenames: Vec<String>,
},
/// Replace the current playlist with new files
Replace {
/// The filenames of the files to replace the playlist with
filenames: Vec<String>,
},
/// Fetch properties of the current playback or playlist
Prop {
/// The properties to fetch
properties: Vec<String>,
},
/// Enter interactive mode to send commands to MPV IPC
Interactive,
/// Generate shell completions
Completion {
#[arg(value_enum, default_value_t = Shell::Bash)]
shell: Shell,
},
}
#[expect(clippy::enum_variant_names)]
#[derive(Clone, ValueEnum)]
pub enum Shell {
Bash,
Elvish,
Fish,
PowerShell,
Zsh,
}
impl Shell {
pub fn generate(&self, app: &mut clap::Command) {
match self {
Self::Bash => {
clap_complete::generate(
clap_complete::Shell::Bash,
app,
"mpvrc",
&mut io::stdout(),
);
}
Self::Elvish => {
clap_complete::generate(
clap_complete::Shell::Elvish,
app,
"mpvrc",
&mut io::stdout(),
);
}
Self::Fish => {
clap_complete::generate(
clap_complete::Shell::Fish,
app,
"mpvrc",
&mut io::stdout(),
);
}
Self::PowerShell => clap_complete::generate(
clap_complete::Shell::PowerShell,
app,
"mpvrc",
&mut io::stdout(),
),
Self::Zsh => {
clap_complete::generate(clap_complete::Shell::Zsh, app, "mpvrc", &mut io::stdout());
}
}
}
}
fn get_socket_path(cli: &Cli) -> String {
cli.socket
.clone()
.unwrap_or_else(|| SOCKET_PATH.to_string())
}
fn confirm(prompt: &str, yes: bool) -> bool {
if yes {
return true;
}
print!("{prompt} [y/N] ");
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
input.trim().eq_ignore_ascii_case("y")
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt::init();
let cli = Cli::parse();
if matches!(cli.command, CommandOptions::Completion { .. }) {
let mut app = Cli::command();
if let CommandOptions::Completion { shell } = &cli.command {
shell.generate(&mut app);
}
return Ok(());
}
let socket_path = get_socket_path(&cli);
if !PathBuf::from(&socket_path).exists() {
debug!(socket_path);
error!(
"Error: MPV socket not found at {}. Is MPV running?",
socket_path
);
return Err(MrcError::ConnectionError(std::io::Error::new(
std::io::ErrorKind::NotFound,
"MPV socket not found",
)));
}
match cli.command {
CommandOptions::Play { index } => {
Commands::play(index, Some(&socket_path)).await?;
}
CommandOptions::Pause => {
Commands::pause(Some(&socket_path)).await?;
}
CommandOptions::Stop => {
if confirm("This will stop playback and quit MPV. Continue?", cli.yes) {
Commands::stop(Some(&socket_path)).await?;
} else {
println!("Cancelled.");
}
}
CommandOptions::Next => {
Commands::next(Some(&socket_path)).await?;
}
CommandOptions::Prev => {
Commands::prev(Some(&socket_path)).await?;
}
CommandOptions::Seek { seconds } => {
Commands::seek_to(seconds.into(), Some(&socket_path)).await?;
}
CommandOptions::Move { index1, index2 } => {
Commands::move_item(index1, index2, Some(&socket_path)).await?;
}
CommandOptions::Remove { index } => {
if confirm(
&format!("This will remove item at index {index:?}. Continue?"),
cli.yes,
) {
Commands::remove_item(index, Some(&socket_path)).await?;
} else {
println!("Cancelled.");
}
}
CommandOptions::Clear => {
if confirm("This will clear the entire playlist. Continue?", cli.yes) {
Commands::clear_playlist(Some(&socket_path)).await?;
} else {
println!("Cancelled.");
}
}
CommandOptions::List => {
Commands::list_playlist(Some(&socket_path)).await?;
}
CommandOptions::Add { filenames } => {
Commands::add_files(&filenames, Some(&socket_path)).await?;
}
CommandOptions::Replace { filenames } => {
Commands::replace_playlist(&filenames, Some(&socket_path)).await?;
}
CommandOptions::Prop { properties } => {
Commands::get_properties(&properties, Some(&socket_path)).await?;
}
CommandOptions::Interactive => {
InteractiveMode::run(Some(&socket_path)).await?;
}
CommandOptions::Completion { .. } => unreachable!(),
}
Ok(())
}

342
src/cli/cli.rs.html Normal file
View file

@ -0,0 +1,342 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/cli.rs`."><title>cli.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../../" data-static-root-path="../../static.files/" data-current-crate="cli" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../../static.files/storage-82c7156e.js"></script><script defer src="../../static.files/src-script-63605ae7.js"></script><script defer src="../../src-files.js"></script><script defer src="../../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer"></div><main><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1><div class="sub-heading">cli/</div>cli.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap digits-3"><pre class="rust"><code><a href=#1 id=1 data-nosnippet>1</a><span class="kw">use </span>clap::{Parser, Subcommand};
<a href=#2 id=2 data-nosnippet>2</a><span class="kw">use </span>mrc::set_property;
<a href=#3 id=3 data-nosnippet>3</a><span class="kw">use </span>mrc::SOCKET_PATH;
<a href=#4 id=4 data-nosnippet>4</a><span class="kw">use </span>mrc::{
<a href=#5 id=5 data-nosnippet>5</a> get_property, loadfile, playlist_clear, playlist_move, playlist_next, playlist_prev,
<a href=#6 id=6 data-nosnippet>6</a> playlist_remove, quit, seek,
<a href=#7 id=7 data-nosnippet>7</a>};
<a href=#8 id=8 data-nosnippet>8</a><span class="kw">use </span>serde_json::json;
<a href=#9 id=9 data-nosnippet>9</a><span class="kw">use </span>std::io::{<span class="self">self</span>, Write};
<a href=#10 id=10 data-nosnippet>10</a><span class="kw">use </span>std::path::PathBuf;
<a href=#11 id=11 data-nosnippet>11</a><span class="kw">use </span>tracing::{debug, error, info};
<a href=#12 id=12 data-nosnippet>12</a>
<a href=#13 id=13 data-nosnippet>13</a><span class="attr">#[derive(Parser)]
<a href=#14 id=14 data-nosnippet>14</a>#[command(author, version, about)]
<a href=#15 id=15 data-nosnippet>15</a></span><span class="kw">struct </span>Cli {
<a href=#16 id=16 data-nosnippet>16</a> <span class="attr">#[arg(short, long, global = <span class="bool-val">true</span>)]
<a href=#17 id=17 data-nosnippet>17</a> </span>debug: bool,
<a href=#18 id=18 data-nosnippet>18</a>
<a href=#19 id=19 data-nosnippet>19</a> <span class="attr">#[command(subcommand)]
<a href=#20 id=20 data-nosnippet>20</a> </span>command: CommandOptions,
<a href=#21 id=21 data-nosnippet>21</a>}
<a href=#22 id=22 data-nosnippet>22</a>
<a href=#23 id=23 data-nosnippet>23</a><span class="attr">#[derive(Subcommand)]
<a href=#24 id=24 data-nosnippet>24</a></span><span class="kw">enum </span>CommandOptions {
<a href=#25 id=25 data-nosnippet>25</a> <span class="doccomment">/// Play media at the specified index in the playlist
<a href=#26 id=26 data-nosnippet>26</a> </span>Play {
<a href=#27 id=27 data-nosnippet>27</a> <span class="doccomment">/// The index of the media to play
<a href=#28 id=28 data-nosnippet>28</a> </span>index: <span class="prelude-ty">Option</span>&lt;usize&gt;,
<a href=#29 id=29 data-nosnippet>29</a> },
<a href=#30 id=30 data-nosnippet>30</a>
<a href=#31 id=31 data-nosnippet>31</a> <span class="doccomment">/// Pause the currently playing media
<a href=#32 id=32 data-nosnippet>32</a> </span>Pause,
<a href=#33 id=33 data-nosnippet>33</a>
<a href=#34 id=34 data-nosnippet>34</a> <span class="doccomment">/// Stop the playback and quit MPV
<a href=#35 id=35 data-nosnippet>35</a> </span>Stop,
<a href=#36 id=36 data-nosnippet>36</a>
<a href=#37 id=37 data-nosnippet>37</a> <span class="doccomment">/// Skip to the next item in the playlist
<a href=#38 id=38 data-nosnippet>38</a> </span>Next,
<a href=#39 id=39 data-nosnippet>39</a>
<a href=#40 id=40 data-nosnippet>40</a> <span class="doccomment">/// Skip to the previous item in the playlist
<a href=#41 id=41 data-nosnippet>41</a> </span>Prev,
<a href=#42 id=42 data-nosnippet>42</a>
<a href=#43 id=43 data-nosnippet>43</a> <span class="doccomment">/// Seek to a specific position in the currently playing media
<a href=#44 id=44 data-nosnippet>44</a> </span>Seek {
<a href=#45 id=45 data-nosnippet>45</a> <span class="doccomment">/// The number of seconds to seek to
<a href=#46 id=46 data-nosnippet>46</a> </span>seconds: i32,
<a href=#47 id=47 data-nosnippet>47</a> },
<a href=#48 id=48 data-nosnippet>48</a>
<a href=#49 id=49 data-nosnippet>49</a> <span class="doccomment">/// Move an item in the playlist from one index to another
<a href=#50 id=50 data-nosnippet>50</a> </span>Move {
<a href=#51 id=51 data-nosnippet>51</a> <span class="doccomment">/// The index of the item to move
<a href=#52 id=52 data-nosnippet>52</a> </span>index1: usize,
<a href=#53 id=53 data-nosnippet>53</a>
<a href=#54 id=54 data-nosnippet>54</a> <span class="doccomment">/// The index to move the item to
<a href=#55 id=55 data-nosnippet>55</a> </span>index2: usize,
<a href=#56 id=56 data-nosnippet>56</a> },
<a href=#57 id=57 data-nosnippet>57</a>
<a href=#58 id=58 data-nosnippet>58</a> <span class="doccomment">/// Remove an item from the playlist
<a href=#59 id=59 data-nosnippet>59</a> ///
<a href=#60 id=60 data-nosnippet>60</a> /// If invoked while playlist has no entries, or if the only entry
<a href=#61 id=61 data-nosnippet>61</a> /// is the active video, then this will exit MPV.
<a href=#62 id=62 data-nosnippet>62</a> </span>Remove {
<a href=#63 id=63 data-nosnippet>63</a> <span class="doccomment">/// The index of the item to remove (optional)
<a href=#64 id=64 data-nosnippet>64</a> </span>index: <span class="prelude-ty">Option</span>&lt;usize&gt;,
<a href=#65 id=65 data-nosnippet>65</a> },
<a href=#66 id=66 data-nosnippet>66</a>
<a href=#67 id=67 data-nosnippet>67</a> <span class="doccomment">/// Clear the entire playlist
<a href=#68 id=68 data-nosnippet>68</a> </span>Clear,
<a href=#69 id=69 data-nosnippet>69</a>
<a href=#70 id=70 data-nosnippet>70</a> <span class="doccomment">/// List all the items in the playlist
<a href=#71 id=71 data-nosnippet>71</a> </span>List,
<a href=#72 id=72 data-nosnippet>72</a>
<a href=#73 id=73 data-nosnippet>73</a> <span class="doccomment">/// Add files to the playlist
<a href=#74 id=74 data-nosnippet>74</a> ///
<a href=#75 id=75 data-nosnippet>75</a> /// Needs at least one file to be passed.
<a href=#76 id=76 data-nosnippet>76</a> </span>Add {
<a href=#77 id=77 data-nosnippet>77</a> <span class="doccomment">/// The filenames of the files to add
<a href=#78 id=78 data-nosnippet>78</a> </span>filenames: Vec&lt;String&gt;,
<a href=#79 id=79 data-nosnippet>79</a> },
<a href=#80 id=80 data-nosnippet>80</a>
<a href=#81 id=81 data-nosnippet>81</a> <span class="doccomment">/// Replace the current playlist with new files
<a href=#82 id=82 data-nosnippet>82</a> </span>Replace {
<a href=#83 id=83 data-nosnippet>83</a> <span class="doccomment">/// The filenames of the files to replace the playlist with
<a href=#84 id=84 data-nosnippet>84</a> </span>filenames: Vec&lt;String&gt;,
<a href=#85 id=85 data-nosnippet>85</a> },
<a href=#86 id=86 data-nosnippet>86</a>
<a href=#87 id=87 data-nosnippet>87</a> <span class="doccomment">/// Fetch properties of the current playback or playlist
<a href=#88 id=88 data-nosnippet>88</a> </span>Prop {
<a href=#89 id=89 data-nosnippet>89</a> <span class="doccomment">/// The properties to fetch
<a href=#90 id=90 data-nosnippet>90</a> </span>properties: Vec&lt;String&gt;,
<a href=#91 id=91 data-nosnippet>91</a> },
<a href=#92 id=92 data-nosnippet>92</a>
<a href=#93 id=93 data-nosnippet>93</a> <span class="doccomment">/// Enter interactive mode to send commands to MPV IPC
<a href=#94 id=94 data-nosnippet>94</a> </span>Interactive,
<a href=#95 id=95 data-nosnippet>95</a>}
<a href=#96 id=96 data-nosnippet>96</a>
<a href=#97 id=97 data-nosnippet>97</a><span class="attr">#[tokio::main]
<a href=#98 id=98 data-nosnippet>98</a></span><span class="kw">async fn </span>main() -&gt; io::Result&lt;()&gt; {
<a href=#99 id=99 data-nosnippet>99</a> tracing_subscriber::fmt::init();
<a href=#100 id=100 data-nosnippet>100</a> <span class="kw">let </span>cli = Cli::parse();
<a href=#101 id=101 data-nosnippet>101</a>
<a href=#102 id=102 data-nosnippet>102</a> <span class="kw">if </span>!PathBuf::from(SOCKET_PATH).exists() {
<a href=#103 id=103 data-nosnippet>103</a> <span class="macro">debug!</span>(SOCKET_PATH);
<a href=#104 id=104 data-nosnippet>104</a> <span class="macro">error!</span>(<span class="string">"Error: MPV socket not found. Is MPV running?"</span>);
<a href=#105 id=105 data-nosnippet>105</a> <span class="kw">return </span><span class="prelude-val">Ok</span>(());
<a href=#106 id=106 data-nosnippet>106</a> }
<a href=#107 id=107 data-nosnippet>107</a>
<a href=#108 id=108 data-nosnippet>108</a> <span class="kw">match </span>cli.command {
<a href=#109 id=109 data-nosnippet>109</a> CommandOptions::Play { index } =&gt; {
<a href=#110 id=110 data-nosnippet>110</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(idx) = index {
<a href=#111 id=111 data-nosnippet>111</a> <span class="macro">info!</span>(<span class="string">"Playing media at index: {}"</span>, idx);
<a href=#112 id=112 data-nosnippet>112</a> set_property(<span class="string">"playlist-pos"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(idx), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#113 id=113 data-nosnippet>113</a> }
<a href=#114 id=114 data-nosnippet>114</a> <span class="macro">info!</span>(<span class="string">"Unpausing playback"</span>);
<a href=#115 id=115 data-nosnippet>115</a> set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">false</span>), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#116 id=116 data-nosnippet>116</a> }
<a href=#117 id=117 data-nosnippet>117</a>
<a href=#118 id=118 data-nosnippet>118</a> CommandOptions::Pause =&gt; {
<a href=#119 id=119 data-nosnippet>119</a> <span class="macro">info!</span>(<span class="string">"Pausing playback"</span>);
<a href=#120 id=120 data-nosnippet>120</a> set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">true</span>), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#121 id=121 data-nosnippet>121</a> }
<a href=#122 id=122 data-nosnippet>122</a>
<a href=#123 id=123 data-nosnippet>123</a> CommandOptions::Stop =&gt; {
<a href=#124 id=124 data-nosnippet>124</a> <span class="macro">info!</span>(<span class="string">"Stopping playback and quitting MPV"</span>);
<a href=#125 id=125 data-nosnippet>125</a> quit(<span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#126 id=126 data-nosnippet>126</a> }
<a href=#127 id=127 data-nosnippet>127</a>
<a href=#128 id=128 data-nosnippet>128</a> CommandOptions::Next =&gt; {
<a href=#129 id=129 data-nosnippet>129</a> <span class="macro">info!</span>(<span class="string">"Skipping to next item in the playlist"</span>);
<a href=#130 id=130 data-nosnippet>130</a> playlist_next(<span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#131 id=131 data-nosnippet>131</a> }
<a href=#132 id=132 data-nosnippet>132</a>
<a href=#133 id=133 data-nosnippet>133</a> CommandOptions::Prev =&gt; {
<a href=#134 id=134 data-nosnippet>134</a> <span class="macro">info!</span>(<span class="string">"Skipping to previous item in the playlist"</span>);
<a href=#135 id=135 data-nosnippet>135</a> playlist_prev(<span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#136 id=136 data-nosnippet>136</a> }
<a href=#137 id=137 data-nosnippet>137</a>
<a href=#138 id=138 data-nosnippet>138</a> CommandOptions::Seek { seconds } =&gt; {
<a href=#139 id=139 data-nosnippet>139</a> <span class="macro">info!</span>(<span class="string">"Seeking to {} seconds"</span>, seconds);
<a href=#140 id=140 data-nosnippet>140</a> seek(seconds.into(), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#141 id=141 data-nosnippet>141</a> }
<a href=#142 id=142 data-nosnippet>142</a>
<a href=#143 id=143 data-nosnippet>143</a> CommandOptions::Move { index1, index2 } =&gt; {
<a href=#144 id=144 data-nosnippet>144</a> <span class="macro">info!</span>(<span class="string">"Moving item from index {} to {}"</span>, index1, index2);
<a href=#145 id=145 data-nosnippet>145</a> playlist_move(index1, index2, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#146 id=146 data-nosnippet>146</a> }
<a href=#147 id=147 data-nosnippet>147</a>
<a href=#148 id=148 data-nosnippet>148</a> CommandOptions::Remove { index } =&gt; {
<a href=#149 id=149 data-nosnippet>149</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(idx) = index {
<a href=#150 id=150 data-nosnippet>150</a> <span class="macro">info!</span>(<span class="string">"Removing item at index {}"</span>, idx);
<a href=#151 id=151 data-nosnippet>151</a> playlist_remove(<span class="prelude-val">Some</span>(idx), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#152 id=152 data-nosnippet>152</a> } <span class="kw">else </span>{
<a href=#153 id=153 data-nosnippet>153</a> <span class="macro">info!</span>(<span class="string">"Removing current item from playlist"</span>);
<a href=#154 id=154 data-nosnippet>154</a> playlist_remove(<span class="prelude-val">None</span>, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#155 id=155 data-nosnippet>155</a> }
<a href=#156 id=156 data-nosnippet>156</a> }
<a href=#157 id=157 data-nosnippet>157</a>
<a href=#158 id=158 data-nosnippet>158</a> CommandOptions::Clear =&gt; {
<a href=#159 id=159 data-nosnippet>159</a> <span class="macro">info!</span>(<span class="string">"Clearing the playlist"</span>);
<a href=#160 id=160 data-nosnippet>160</a> playlist_clear(<span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#161 id=161 data-nosnippet>161</a> }
<a href=#162 id=162 data-nosnippet>162</a>
<a href=#163 id=163 data-nosnippet>163</a> CommandOptions::List =&gt; {
<a href=#164 id=164 data-nosnippet>164</a> <span class="macro">info!</span>(<span class="string">"Listing playlist items"</span>);
<a href=#165 id=165 data-nosnippet>165</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(data) = get_property(<span class="string">"playlist"</span>, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">? </span>{
<a href=#166 id=166 data-nosnippet>166</a> <span class="macro">println!</span>(<span class="string">"{}"</span>, serde_json::to_string_pretty(<span class="kw-2">&amp;</span>data)<span class="question-mark">?</span>);
<a href=#167 id=167 data-nosnippet>167</a> }
<a href=#168 id=168 data-nosnippet>168</a> }
<a href=#169 id=169 data-nosnippet>169</a>
<a href=#170 id=170 data-nosnippet>170</a> CommandOptions::Add { filenames } =&gt; {
<a href=#171 id=171 data-nosnippet>171</a> <span class="kw">if </span>filenames.is_empty() {
<a href=#172 id=172 data-nosnippet>172</a> <span class="kw">let </span>e = <span class="string">"No files provided to add to the playlist"</span>;
<a href=#173 id=173 data-nosnippet>173</a> <span class="macro">error!</span>(<span class="string">"{}"</span>, e);
<a href=#174 id=174 data-nosnippet>174</a> <span class="kw">return </span><span class="prelude-val">Err</span>(io::Error::new(io::ErrorKind::InvalidInput, e));
<a href=#175 id=175 data-nosnippet>175</a> }
<a href=#176 id=176 data-nosnippet>176</a>
<a href=#177 id=177 data-nosnippet>177</a> <span class="macro">info!</span>(<span class="string">"Adding {} files to the playlist"</span>, filenames.len());
<a href=#178 id=178 data-nosnippet>178</a> <span class="kw">for </span>filename <span class="kw">in </span>filenames {
<a href=#179 id=179 data-nosnippet>179</a> loadfile(<span class="kw-2">&amp;</span>filename, <span class="bool-val">true</span>, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#180 id=180 data-nosnippet>180</a> }
<a href=#181 id=181 data-nosnippet>181</a> }
<a href=#182 id=182 data-nosnippet>182</a>
<a href=#183 id=183 data-nosnippet>183</a> CommandOptions::Replace { filenames } =&gt; {
<a href=#184 id=184 data-nosnippet>184</a> <span class="macro">info!</span>(<span class="string">"Replacing current playlist with {} files"</span>, filenames.len());
<a href=#185 id=185 data-nosnippet>185</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(first_file) = filenames.first() {
<a href=#186 id=186 data-nosnippet>186</a> loadfile(first_file, <span class="bool-val">false</span>, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#187 id=187 data-nosnippet>187</a> <span class="kw">for </span>filename <span class="kw">in </span><span class="kw-2">&amp;</span>filenames[<span class="number">1</span>..] {
<a href=#188 id=188 data-nosnippet>188</a> loadfile(filename, <span class="bool-val">true</span>, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#189 id=189 data-nosnippet>189</a> }
<a href=#190 id=190 data-nosnippet>190</a> }
<a href=#191 id=191 data-nosnippet>191</a> }
<a href=#192 id=192 data-nosnippet>192</a>
<a href=#193 id=193 data-nosnippet>193</a> CommandOptions::Prop { properties } =&gt; {
<a href=#194 id=194 data-nosnippet>194</a> <span class="macro">info!</span>(<span class="string">"Fetching properties: {:?}"</span>, properties);
<a href=#195 id=195 data-nosnippet>195</a> <span class="kw">for </span>property <span class="kw">in </span>properties {
<a href=#196 id=196 data-nosnippet>196</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(data) = get_property(<span class="kw-2">&amp;</span>property, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">? </span>{
<a href=#197 id=197 data-nosnippet>197</a> <span class="macro">println!</span>(<span class="string">"{property}: {data}"</span>);
<a href=#198 id=198 data-nosnippet>198</a> }
<a href=#199 id=199 data-nosnippet>199</a> }
<a href=#200 id=200 data-nosnippet>200</a> }
<a href=#201 id=201 data-nosnippet>201</a>
<a href=#202 id=202 data-nosnippet>202</a> CommandOptions::Interactive =&gt; {
<a href=#203 id=203 data-nosnippet>203</a> <span class="macro">println!</span>(<span class="string">"Entering interactive mode. Type 'exit' to quit."</span>);
<a href=#204 id=204 data-nosnippet>204</a> <span class="kw">let </span>stdin = io::stdin();
<a href=#205 id=205 data-nosnippet>205</a> <span class="kw">let </span><span class="kw-2">mut </span>stdout = io::stdout();
<a href=#206 id=206 data-nosnippet>206</a>
<a href=#207 id=207 data-nosnippet>207</a> <span class="kw">loop </span>{
<a href=#208 id=208 data-nosnippet>208</a> <span class="macro">print!</span>(<span class="string">"mpv&gt; "</span>);
<a href=#209 id=209 data-nosnippet>209</a> stdout.flush()<span class="question-mark">?</span>;
<a href=#210 id=210 data-nosnippet>210</a> <span class="kw">let </span><span class="kw-2">mut </span>input = String::new();
<a href=#211 id=211 data-nosnippet>211</a> stdin.read_line(<span class="kw-2">&amp;mut </span>input)<span class="question-mark">?</span>;
<a href=#212 id=212 data-nosnippet>212</a> <span class="kw">let </span>trimmed = input.trim();
<a href=#213 id=213 data-nosnippet>213</a>
<a href=#214 id=214 data-nosnippet>214</a> <span class="kw">if </span>trimmed.eq_ignore_ascii_case(<span class="string">"exit"</span>) {
<a href=#215 id=215 data-nosnippet>215</a> <span class="macro">println!</span>(<span class="string">"Exiting interactive mode."</span>);
<a href=#216 id=216 data-nosnippet>216</a> <span class="kw">break</span>;
<a href=#217 id=217 data-nosnippet>217</a> }
<a href=#218 id=218 data-nosnippet>218</a>
<a href=#219 id=219 data-nosnippet>219</a> <span class="comment">// I don't like this either, but it looks cleaner than a multi-line
<a href=#220 id=220 data-nosnippet>220</a> // print macro just cramped in here.
<a href=#221 id=221 data-nosnippet>221</a> </span><span class="kw">let </span>commands = <span class="macro">vec!</span>[
<a href=#222 id=222 data-nosnippet>222</a> (
<a href=#223 id=223 data-nosnippet>223</a> <span class="string">"play [index]"</span>,
<a href=#224 id=224 data-nosnippet>224</a> <span class="string">"Play or unpause playback, optionally at the specified index"</span>,
<a href=#225 id=225 data-nosnippet>225</a> ),
<a href=#226 id=226 data-nosnippet>226</a> (<span class="string">"pause"</span>, <span class="string">"Pause playback"</span>),
<a href=#227 id=227 data-nosnippet>227</a> (<span class="string">"stop"</span>, <span class="string">"Stop playback and quit MPV"</span>),
<a href=#228 id=228 data-nosnippet>228</a> (<span class="string">"next"</span>, <span class="string">"Skip to the next item in the playlist"</span>),
<a href=#229 id=229 data-nosnippet>229</a> (<span class="string">"prev"</span>, <span class="string">"Skip to the previous item in the playlist"</span>),
<a href=#230 id=230 data-nosnippet>230</a> (<span class="string">"seek &lt;seconds&gt;"</span>, <span class="string">"Seek to the specified position"</span>),
<a href=#231 id=231 data-nosnippet>231</a> (<span class="string">"clear"</span>, <span class="string">"Clear the playlist"</span>),
<a href=#232 id=232 data-nosnippet>232</a> (<span class="string">"list"</span>, <span class="string">"List all items in the playlist"</span>),
<a href=#233 id=233 data-nosnippet>233</a> (<span class="string">"add &lt;files&gt;"</span>, <span class="string">"Add files to the playlist"</span>),
<a href=#234 id=234 data-nosnippet>234</a> (<span class="string">"get &lt;property&gt;"</span>, <span class="string">"Get the specified property"</span>),
<a href=#235 id=235 data-nosnippet>235</a> (
<a href=#236 id=236 data-nosnippet>236</a> <span class="string">"set &lt;property&gt; &lt;value&gt;"</span>,
<a href=#237 id=237 data-nosnippet>237</a> <span class="string">"Set the specified property to a value"</span>,
<a href=#238 id=238 data-nosnippet>238</a> ),
<a href=#239 id=239 data-nosnippet>239</a> (<span class="string">"exit"</span>, <span class="string">"Quit interactive mode"</span>),
<a href=#240 id=240 data-nosnippet>240</a> ];
<a href=#241 id=241 data-nosnippet>241</a>
<a href=#242 id=242 data-nosnippet>242</a> <span class="kw">if </span>trimmed.eq_ignore_ascii_case(<span class="string">"help"</span>) {
<a href=#243 id=243 data-nosnippet>243</a> <span class="macro">println!</span>(<span class="string">"Valid commands:"</span>);
<a href=#244 id=244 data-nosnippet>244</a> <span class="kw">for </span>(command, description) <span class="kw">in </span>commands {
<a href=#245 id=245 data-nosnippet>245</a> <span class="macro">println!</span>(<span class="string">" {} - {}"</span>, command, description);
<a href=#246 id=246 data-nosnippet>246</a> }
<a href=#247 id=247 data-nosnippet>247</a> <span class="kw">continue</span>;
<a href=#248 id=248 data-nosnippet>248</a> }
<a href=#249 id=249 data-nosnippet>249</a>
<a href=#250 id=250 data-nosnippet>250</a> <span class="kw">let </span>parts: Vec&lt;<span class="kw-2">&amp;</span>str&gt; = trimmed.split_whitespace().collect();
<a href=#251 id=251 data-nosnippet>251</a> <span class="kw">match </span>parts.as_slice() {
<a href=#252 id=252 data-nosnippet>252</a> [<span class="string">"play"</span>] =&gt; {
<a href=#253 id=253 data-nosnippet>253</a> <span class="macro">info!</span>(<span class="string">"Unpausing playback"</span>);
<a href=#254 id=254 data-nosnippet>254</a> set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">false</span>), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#255 id=255 data-nosnippet>255</a> }
<a href=#256 id=256 data-nosnippet>256</a>
<a href=#257 id=257 data-nosnippet>257</a> [<span class="string">"play"</span>, index] =&gt; {
<a href=#258 id=258 data-nosnippet>258</a> <span class="kw">if let </span><span class="prelude-val">Ok</span>(idx) = index.parse::&lt;usize&gt;() {
<a href=#259 id=259 data-nosnippet>259</a> <span class="macro">info!</span>(<span class="string">"Playing media at index: {}"</span>, idx);
<a href=#260 id=260 data-nosnippet>260</a> set_property(<span class="string">"playlist-pos"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(idx), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#261 id=261 data-nosnippet>261</a> set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">false</span>), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#262 id=262 data-nosnippet>262</a> } <span class="kw">else </span>{
<a href=#263 id=263 data-nosnippet>263</a> <span class="macro">println!</span>(<span class="string">"Invalid index: {}"</span>, index);
<a href=#264 id=264 data-nosnippet>264</a> }
<a href=#265 id=265 data-nosnippet>265</a> }
<a href=#266 id=266 data-nosnippet>266</a>
<a href=#267 id=267 data-nosnippet>267</a> [<span class="string">"pause"</span>] =&gt; {
<a href=#268 id=268 data-nosnippet>268</a> <span class="macro">info!</span>(<span class="string">"Pausing playback"</span>);
<a href=#269 id=269 data-nosnippet>269</a> set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">true</span>), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#270 id=270 data-nosnippet>270</a> }
<a href=#271 id=271 data-nosnippet>271</a>
<a href=#272 id=272 data-nosnippet>272</a> [<span class="string">"stop"</span>] =&gt; {
<a href=#273 id=273 data-nosnippet>273</a> <span class="macro">info!</span>(<span class="string">"Pausing playback"</span>);
<a href=#274 id=274 data-nosnippet>274</a> quit(<span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#275 id=275 data-nosnippet>275</a> }
<a href=#276 id=276 data-nosnippet>276</a>
<a href=#277 id=277 data-nosnippet>277</a> [<span class="string">"next"</span>] =&gt; {
<a href=#278 id=278 data-nosnippet>278</a> <span class="macro">info!</span>(<span class="string">"Skipping to next item in the playlist"</span>);
<a href=#279 id=279 data-nosnippet>279</a> playlist_next(<span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#280 id=280 data-nosnippet>280</a> }
<a href=#281 id=281 data-nosnippet>281</a>
<a href=#282 id=282 data-nosnippet>282</a> [<span class="string">"prev"</span>] =&gt; {
<a href=#283 id=283 data-nosnippet>283</a> <span class="macro">info!</span>(<span class="string">"Skipping to previous item in the playlist"</span>);
<a href=#284 id=284 data-nosnippet>284</a> playlist_prev(<span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#285 id=285 data-nosnippet>285</a> }
<a href=#286 id=286 data-nosnippet>286</a>
<a href=#287 id=287 data-nosnippet>287</a> [<span class="string">"seek"</span>, seconds] =&gt; {
<a href=#288 id=288 data-nosnippet>288</a> <span class="kw">if let </span><span class="prelude-val">Ok</span>(sec) = seconds.parse::&lt;i32&gt;() {
<a href=#289 id=289 data-nosnippet>289</a> <span class="macro">info!</span>(<span class="string">"Seeking to {} seconds"</span>, sec);
<a href=#290 id=290 data-nosnippet>290</a> seek(sec.into(), <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#291 id=291 data-nosnippet>291</a> } <span class="kw">else </span>{
<a href=#292 id=292 data-nosnippet>292</a> <span class="macro">println!</span>(<span class="string">"Invalid seconds: {}"</span>, seconds);
<a href=#293 id=293 data-nosnippet>293</a> }
<a href=#294 id=294 data-nosnippet>294</a> }
<a href=#295 id=295 data-nosnippet>295</a>
<a href=#296 id=296 data-nosnippet>296</a> [<span class="string">"clear"</span>] =&gt; {
<a href=#297 id=297 data-nosnippet>297</a> <span class="macro">info!</span>(<span class="string">"Clearing the playlist"</span>);
<a href=#298 id=298 data-nosnippet>298</a> playlist_clear(<span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#299 id=299 data-nosnippet>299</a> }
<a href=#300 id=300 data-nosnippet>300</a>
<a href=#301 id=301 data-nosnippet>301</a> [<span class="string">"list"</span>] =&gt; {
<a href=#302 id=302 data-nosnippet>302</a> <span class="macro">info!</span>(<span class="string">"Listing playlist items"</span>);
<a href=#303 id=303 data-nosnippet>303</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(data) = get_property(<span class="string">"playlist"</span>, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">? </span>{
<a href=#304 id=304 data-nosnippet>304</a> <span class="macro">println!</span>(<span class="string">"{}"</span>, serde_json::to_string_pretty(<span class="kw-2">&amp;</span>data)<span class="question-mark">?</span>);
<a href=#305 id=305 data-nosnippet>305</a> }
<a href=#306 id=306 data-nosnippet>306</a> }
<a href=#307 id=307 data-nosnippet>307</a>
<a href=#308 id=308 data-nosnippet>308</a> [<span class="string">"add"</span>, files @ ..] =&gt; {
<a href=#309 id=309 data-nosnippet>309</a> <span class="kw">if </span>files.is_empty() {
<a href=#310 id=310 data-nosnippet>310</a> <span class="macro">println!</span>(<span class="string">"No files provided to add to the playlist"</span>);
<a href=#311 id=311 data-nosnippet>311</a> } <span class="kw">else </span>{
<a href=#312 id=312 data-nosnippet>312</a> <span class="macro">info!</span>(<span class="string">"Adding {} files to the playlist"</span>, files.len());
<a href=#313 id=313 data-nosnippet>313</a> <span class="kw">for </span>file <span class="kw">in </span>files {
<a href=#314 id=314 data-nosnippet>314</a> loadfile(file, <span class="bool-val">true</span>, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#315 id=315 data-nosnippet>315</a> }
<a href=#316 id=316 data-nosnippet>316</a> }
<a href=#317 id=317 data-nosnippet>317</a> }
<a href=#318 id=318 data-nosnippet>318</a>
<a href=#319 id=319 data-nosnippet>319</a> [<span class="string">"get"</span>, property] =&gt; {
<a href=#320 id=320 data-nosnippet>320</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(data) = get_property(property, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">? </span>{
<a href=#321 id=321 data-nosnippet>321</a> <span class="macro">println!</span>(<span class="string">"{property}: {data}"</span>);
<a href=#322 id=322 data-nosnippet>322</a> }
<a href=#323 id=323 data-nosnippet>323</a> }
<a href=#324 id=324 data-nosnippet>324</a>
<a href=#325 id=325 data-nosnippet>325</a> [<span class="string">"set"</span>, property, value] =&gt; {
<a href=#326 id=326 data-nosnippet>326</a> <span class="kw">let </span>json_value = serde_json::from_str::&lt;serde_json::Value&gt;(value)
<a href=#327 id=327 data-nosnippet>327</a> .unwrap_or_else(|<span class="kw">_</span>| <span class="macro">json!</span>(value));
<a href=#328 id=328 data-nosnippet>328</a> set_property(property, <span class="kw-2">&amp;</span>json_value, <span class="prelude-val">None</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#329 id=329 data-nosnippet>329</a> <span class="macro">println!</span>(<span class="string">"Set {property} to {value}"</span>);
<a href=#330 id=330 data-nosnippet>330</a> }
<a href=#331 id=331 data-nosnippet>331</a>
<a href=#332 id=332 data-nosnippet>332</a> <span class="kw">_ </span>=&gt; {
<a href=#333 id=333 data-nosnippet>333</a> <span class="macro">println!</span>(<span class="string">"Unknown command: {}"</span>, trimmed);
<a href=#334 id=334 data-nosnippet>334</a> <span class="macro">println!</span>(<span class="string">"Valid commands: play &lt;index&gt;, pause, stop, next, prev, seek &lt;seconds&gt;, clear, list, add &lt;files&gt;, get &lt;property&gt;, set &lt;property&gt; &lt;value&gt;, exit"</span>);
<a href=#335 id=335 data-nosnippet>335</a> }
<a href=#336 id=336 data-nosnippet>336</a> }
<a href=#337 id=337 data-nosnippet>337</a> }
<a href=#338 id=338 data-nosnippet>338</a> }
<a href=#339 id=339 data-nosnippet>339</a> }
<a href=#340 id=340 data-nosnippet>340</a>
<a href=#341 id=341 data-nosnippet>341</a> <span class="prelude-val">Ok</span>(())
<a href=#342 id=342 data-nosnippet>342</a>}</code></pre></div></section></main></body></html>

View file

@ -1,337 +0,0 @@
//! Command processing module for MRC.
//!
//! # Examples
//!
//! ```rust
//! use mpvrc::commands::Commands;
//!
//! # async fn example() -> mpvrc::Result<()> {
//! // Play media at a specific playlist index
//! Commands::play(Some(0), None).await;
//!
//! // Pause playback
//! Commands::pause(None).await;
//!
//! // Add files to playlist
//! let files = vec!["movie1.mp4".to_string(), "movie2.mp4".to_string()];
//! Commands::add_files(&files, None).await;
//! # Ok(())
//! # }
//! ```
use serde_json::json;
use tracing::info;
use crate::{
MrcError, Result, get_property, loadfile, playlist_clear, playlist_move, playlist_next,
playlist_prev, playlist_remove, quit, seek, set_property,
};
/// Centralized command processing for MPV operations.
pub struct Commands;
impl Commands {
/// Plays media, optionally at a specific playlist index.
///
/// If an index is provided, seeks to that position in the playlist first.
/// Always unpauses playback regardless of whether an index is specified.
///
/// # Arguments
///
/// * `index` - Optional playlist index to play from
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn play(index: Option<usize>, socket_path: Option<&str>) -> Result<()> {
if let Some(idx) = index {
info!("Playing media at index: {}", idx);
set_property("playlist-pos", &json!(idx), socket_path).await?;
}
info!("Unpausing playback");
set_property("pause", &json!(false), socket_path).await?;
Ok(())
}
/// Pauses the currently playing media.
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn pause(socket_path: Option<&str>) -> Result<()> {
info!("Pausing playback");
set_property("pause", &json!(true), socket_path).await?;
Ok(())
}
/// Stops playback and quits MPV.
///
/// This is a destructive operation that will terminate the MPV process.
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn stop(socket_path: Option<&str>) -> Result<()> {
info!("Stopping playback and quitting MPV");
quit(socket_path).await?;
Ok(())
}
/// Advances to the next item in the playlist.
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn next(socket_path: Option<&str>) -> Result<()> {
info!("Skipping to next item in the playlist");
playlist_next(socket_path).await?;
Ok(())
}
/// Goes back to the previous item in the playlist.
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn prev(socket_path: Option<&str>) -> Result<()> {
info!("Skipping to previous item in the playlist");
playlist_prev(socket_path).await?;
Ok(())
}
/// Seeks to a specific position in the currently playing media.
///
/// # Arguments
///
/// * `seconds` - The position in seconds to seek to
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn seek_to(seconds: f64, socket_path: Option<&str>) -> Result<()> {
info!("Seeking to {} seconds", seconds);
seek(seconds, socket_path).await?;
Ok(())
}
/// Moves an item in the playlist from one index to another.
///
/// # Arguments
///
/// * `from_index` - The current index of the item
/// * `to_index` - The desired new index for the item
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn move_item(
from_index: usize,
to_index: usize,
socket_path: Option<&str>,
) -> Result<()> {
info!("Moving item from index {} to {}", from_index, to_index);
playlist_move(from_index, to_index, socket_path).await?;
Ok(())
}
/// Removes an item from the playlist.
///
/// If no index is provided, removes the currently playing item.
///
/// # Arguments
///
/// * `index` - Optional specific index to remove
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn remove_item(index: Option<usize>, socket_path: Option<&str>) -> Result<()> {
if let Some(idx) = index {
info!("Removing item at index {}", idx);
playlist_remove(Some(idx), socket_path).await?;
} else {
info!("Removing current item from playlist");
playlist_remove(None, socket_path).await?;
}
Ok(())
}
/// Clears the entire playlist.
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn clear_playlist(socket_path: Option<&str>) -> Result<()> {
info!("Clearing the playlist");
playlist_clear(socket_path).await?;
Ok(())
}
/// Lists all items in the playlist.
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn list_playlist(socket_path: Option<&str>) -> Result<()> {
info!("Listing playlist items");
if let Some(data) = get_property("playlist", socket_path).await? {
print_playlist(&data);
}
Ok(())
}
/// Adds files to the playlist.
///
/// # Arguments
///
/// * `filenames` - List of file paths to add
///
/// # Errors
///
/// Returns an error if no files are provided or socket communication fails.
pub async fn add_files(filenames: &[String], socket_path: Option<&str>) -> Result<()> {
if filenames.is_empty() {
let e = "No files provided to add to the playlist";
return Err(MrcError::InvalidInput(e.to_string()));
}
info!("Adding {} files to the playlist", filenames.len());
for filename in filenames {
loadfile(filename, true, socket_path).await?;
}
Ok(())
}
/// Replaces the current playlist with new files.
///
/// # Arguments
///
/// * `filenames` - List of file paths to replace the playlist with
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn replace_playlist(filenames: &[String], socket_path: Option<&str>) -> Result<()> {
info!("Replacing current playlist with {} files", filenames.len());
if let Some(first_file) = filenames.first() {
loadfile(first_file, false, socket_path).await?;
for filename in &filenames[1..] {
loadfile(filename, true, socket_path).await?;
}
}
Ok(())
}
/// Fetches multiple properties and prints them.
///
/// # Arguments
///
/// * `properties` - List of property names to fetch
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn get_properties(properties: &[String], socket_path: Option<&str>) -> Result<()> {
info!("Fetching properties: {:?}", properties);
for property in properties {
if let Some(data) = get_property(property, socket_path).await? {
println!("{property}: {data}");
}
}
Ok(())
}
/// Fetches a single property and prints it.
///
/// # Arguments
///
/// * `property` - The property name to fetch
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn get_single_property(property: &str, socket_path: Option<&str>) -> Result<()> {
if let Some(data) = get_property(property, socket_path).await? {
println!("{property}: {data}");
}
Ok(())
}
/// Sets a property and prints a confirmation.
///
/// # Arguments
///
/// * `property` - The property name to set
/// * `value` - The JSON value to set
///
/// # Errors
///
/// Returns an error if the socket communication fails.
pub async fn set_single_property(
property: &str,
value: &serde_json::Value,
socket_path: Option<&str>,
) -> Result<()> {
set_property(property, value, socket_path).await?;
println!("Set {property} to {value}");
Ok(())
}
}
fn print_playlist(data: &serde_json::Value) {
if let Some(items) = data.as_array() {
if items.is_empty() {
println!("Playlist is empty.");
return;
}
for (i, item) in items.iter().enumerate() {
let title = item
.get("title")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let filename = item
.get("filename")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let current = item
.get("current")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
let marker = if current { ">" } else { " " };
println!("{marker} {i:3} | {title} | {filename}");
}
} else {
let pretty_json = serde_json::to_string_pretty(data).unwrap_or_else(|_| data.to_string());
println!("{pretty_json}");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_add_files_empty_list() {
let result = Commands::add_files(&[], None).await;
assert!(result.is_err());
if let Err(MrcError::InvalidInput(msg)) = result {
assert_eq!(msg, "No files provided to add to the playlist");
} else {
panic!("Expected InvalidInput error");
}
}
#[tokio::test]
async fn test_replace_playlist_empty() {
let result = Commands::replace_playlist(&[], None).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_get_properties_empty() {
let result = Commands::get_properties(&[], None).await;
assert!(result.is_ok());
}
}

View file

@ -1,187 +0,0 @@
use std::{io::Error, path::PathBuf};
use rustyline::{Cmd, Config, Editor, KeyEvent, config::EditMode};
use serde_json::json;
use crate::{MrcError, Result, commands::Commands};
pub struct InteractiveMode;
impl InteractiveMode {
/// Runs the interactive mode REPL.
///
/// # Errors
///
/// Returns an error if the socket communication fails or the REPL encounters an error.
pub async fn run(socket_path: Option<&str>) -> Result<()> {
let hist_path = dirs::data_local_dir().map_or_else(
|| PathBuf::from("history.txt"),
|p| p.join("mpvrc").join("history.txt"),
);
let config = Config::builder().edit_mode(EditMode::Vi).build();
let mut rl: Editor<(), _> = Editor::with_config(config)
.map_err(|e| MrcError::ConnectionError(Error::other(e.to_string())))?;
rl.bind_sequence(KeyEvent::ctrl('c'), Cmd::Interrupt);
rl.bind_sequence(KeyEvent::ctrl('l'), Cmd::ClearScreen);
let _ = rl.load_history(&hist_path);
println!("Entering interactive mode. Type 'help' for commands or 'exit' to quit.");
println!("Socket: {}", socket_path.unwrap_or("/tmp/mpvsocket"));
loop {
let readline = rl.readline("mpv> ");
match readline {
Ok(input) => {
let trimmed = input.trim();
if trimmed.is_empty() {
continue;
}
rl.add_history_entry(trimmed).ok();
if trimmed.eq_ignore_ascii_case("exit") {
println!("Exiting interactive mode.");
break;
}
if trimmed.eq_ignore_ascii_case("help") {
Self::show_help();
continue;
}
if let Err(e) = Self::process_command(trimmed, socket_path).await {
eprintln!("Error: {e}");
}
}
Err(rustyline::error::ReadlineError::Interrupted) => {
println!("(interrupted)");
}
Err(rustyline::error::ReadlineError::Eof) => {
println!("Exiting interactive mode.");
break;
}
Err(err) => {
eprintln!("Error: {err:?}");
break;
}
}
}
rl.save_history(&hist_path).ok();
Ok(())
}
fn show_help() {
println!("Available commands:");
let commands = [
(
"play [index]",
"Play or unpause playback, optionally at the specified index",
),
("pause", "Pause playback"),
("stop", "Stop playback and quit MPV"),
("next", "Skip to the next item in the playlist"),
("prev", "Skip to the previous item in the playlist"),
("seek <seconds>", "Seek to the specified position"),
("clear", "Clear the playlist"),
("list", "List all items in the playlist"),
("add <files...>", "Add files to the playlist"),
("get <property>", "Get the specified property"),
(
"set <property> <value>",
"Set the specified property to a value",
),
("help", "Show this help message"),
("exit", "Quit interactive mode"),
];
for (command, description) in commands {
println!(" {command:<22} - {description}");
}
println!("\nKeyboard shortcuts:");
println!(" Ctrl+C - Interrupt current command");
println!(" Ctrl+L - Clear screen");
println!(" Ctrl+D - Exit");
}
async fn process_command(input: &str, socket_path: Option<&str>) -> Result<()> {
let parts: Vec<&str> = input.split_whitespace().collect();
match parts.as_slice() {
["play"] => {
Commands::play(None, socket_path).await?;
}
["play", index] => {
if let Ok(idx) = index.parse::<usize>() {
Commands::play(Some(idx), socket_path).await?;
} else {
println!("Invalid index: {index}");
}
}
["pause"] => {
Commands::pause(socket_path).await?;
}
["stop"] => {
Commands::stop(socket_path).await?;
}
["next"] => {
Commands::next(socket_path).await?;
}
["prev"] => {
Commands::prev(socket_path).await?;
}
["seek", seconds] => {
if let Ok(sec) = seconds.parse::<i32>() {
Commands::seek_to(sec.into(), socket_path).await?;
} else {
println!("Invalid seconds: {seconds}");
}
}
["clear"] => {
Commands::clear_playlist(socket_path).await?;
}
["list"] => {
Commands::list_playlist(socket_path).await?;
}
["add", files @ ..] => {
let file_strings: Vec<String> =
files.iter().map(std::string::ToString::to_string).collect();
if file_strings.is_empty() {
println!("No files provided to add to the playlist");
} else {
Commands::add_files(&file_strings, socket_path).await?;
}
}
["get", property] => {
Commands::get_single_property(property, socket_path).await?;
}
["set", property, value] => {
let json_value = serde_json::from_str::<serde_json::Value>(value)
.unwrap_or_else(|_| json!(value));
Commands::set_single_property(property, &json_value, socket_path).await?;
}
_ => {
println!("Unknown command: {input}");
println!("Type 'help' for a list of available commands.");
}
}
Ok(())
}
}

View file

@ -1,660 +0,0 @@
//! MRC
//! A library for interacting with the MPV media player using its JSON IPC (Inter-Process Communication) protocol.
//!
//! This crate provides a set of utilities to communicate with MPV's IPC socket, enabling you to send commands
//! and retrieve responses in a structured format.
//!
//! ## Features
//!
//! - Send commands to MPV's IPC socket
//! - Retrieve responses in JSON format
//! - Supports common MPV commands like `set_property`, `seek`, and `playlist-next`
//! - Flexible socket path configuration
//!
//! ## Example Usage
//! ```rust
//! use serde_json::json;
//! use tokio;
//! use mpvrc::{send_ipc_command, playlist_next, set_property};
//!
//! #[tokio::main]
//! async fn main() {
//! let result = playlist_next(None).await;
//! match result {
//! Ok(response) => println!("Playlist moved to next: {:?}", response),
//! Err(err) => eprintln!("Error: {:?}", err),
//! }
//!
//! let property_result = set_property("volume", &json!(50), None).await;
//! match property_result {
//! Ok(response) => println!("Volume set: {:?}", response),
//! Err(err) => eprintln!("Error: {:?}", err),
//! }
//! }
//! ```
//!
//! ## Constants
//!
//! ### `SOCKET_PATH`
//! Default path for the MPV IPC socket: `/tmp/mpvsocket`
//!
pub mod commands;
pub mod interactive;
use std::io;
use serde_json::{Value, json};
use thiserror::Error;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::UnixStream,
};
use tracing::debug;
pub const SOCKET_PATH: &str = "/tmp/mpvsocket";
const SOCKET_TIMEOUT_SECS: u64 = 5;
/// Errors that can occur when interacting with the MPV IPC interface.
#[derive(Error, Debug)]
pub enum MrcError {
/// Connection to the MPV socket could not be established.
#[error("failed to connect to MPV socket: {0}")]
ConnectionError(#[from] io::Error),
/// Error when parsing a JSON response from MPV.
#[error("failed to parse JSON response: {0}")]
ParseError(#[from] serde_json::Error),
/// Error when a socket operation times out.
#[error("socket operation timed out after {0} seconds")]
SocketTimeout(u64),
/// Error when MPV returns an error response.
#[error("MPV error: {0}")]
MpvError(String),
/// Error when trying to use a property that doesn't exist.
#[error("property '{0}' not found")]
PropertyNotFound(String),
/// Error when the socket response is not valid UTF-8.
#[error("invalid UTF-8 in socket response: {0}")]
InvalidUtf8(#[from] std::string::FromUtf8Error),
/// Error when a network operation fails.
#[error("network error: {0}")]
NetworkError(String),
/// Error when the server connection is lost or broken.
#[error("server connection lost: {0}")]
ConnectionLost(String),
/// Error when a communication protocol is violated.
#[error("protocol error: {0}")]
ProtocolError(String),
/// Error when invalid input is provided.
#[error("invalid input: {0}")]
InvalidInput(String),
/// Error related to TLS operations.
#[error("TLS error: {0}")]
TlsError(String),
}
/// A specialized Result type for MRC operations.
pub type Result<T> = std::result::Result<T, MrcError>;
/// Connects to the MPV IPC socket with timeout.
async fn connect_to_socket(socket_path: &str) -> Result<UnixStream> {
debug!("Connecting to socket at {}", socket_path);
tokio::time::timeout(
std::time::Duration::from_secs(SOCKET_TIMEOUT_SECS),
UnixStream::connect(socket_path),
)
.await
.map_err(|_| MrcError::SocketTimeout(SOCKET_TIMEOUT_SECS))?
.map_err(MrcError::ConnectionError)
}
/// Sends a command message to the socket with timeout.
async fn send_message(socket: &mut UnixStream, command: &str, args: &[Value]) -> Result<()> {
let mut command_array = vec![json!(command)];
command_array.extend_from_slice(args);
let message = json!({ "command": command_array });
let message_str = format!("{}\n", serde_json::to_string(&message)?);
debug!("Serialized message to send with newline: {}", message_str);
// Write with timeout
tokio::time::timeout(
std::time::Duration::from_secs(SOCKET_TIMEOUT_SECS),
socket.write_all(message_str.as_bytes()),
)
.await
.map_err(|_| MrcError::SocketTimeout(SOCKET_TIMEOUT_SECS))??;
// Flush with timeout
tokio::time::timeout(
std::time::Duration::from_secs(SOCKET_TIMEOUT_SECS),
socket.flush(),
)
.await
.map_err(|_| MrcError::SocketTimeout(SOCKET_TIMEOUT_SECS))??;
debug!("Message sent and flushed");
Ok(())
}
/// Reads and parses the response from the socket.
async fn read_response(socket: &mut UnixStream) -> Result<Value> {
let mut response = vec![0; 1024];
// Read with timeout
let n = tokio::time::timeout(
std::time::Duration::from_secs(SOCKET_TIMEOUT_SECS),
socket.read(&mut response),
)
.await
.map_err(|_| MrcError::SocketTimeout(SOCKET_TIMEOUT_SECS))??;
if n == 0 {
return Err(MrcError::ConnectionLost(
"Socket closed unexpectedly".into(),
));
}
let response_str = String::from_utf8(response[..n].to_vec())?;
debug!("Raw response: {}", response_str);
let json_response =
serde_json::from_str::<Value>(&response_str).map_err(MrcError::ParseError)?;
debug!("Parsed IPC response: {:?}", json_response);
// Check if MPV returned an error
if let Some(error) = json_response.get("error").and_then(|e| e.as_str())
&& !error.is_empty()
{
return Err(MrcError::MpvError(error.to_string()));
}
Ok(json_response)
}
/// Sends a generic IPC command to the specified socket and returns the parsed response data.
///
/// # Arguments
/// - `command`: The name of the command to send to MPV.
/// - `args`: A slice of `Value` arguments to include in the command.
/// - `socket_path`: An optional custom path to the MPV IPC socket. If `None`, the default path is used.
///
/// # Returns
/// A `Result` containing an `Option<Value>` with the parsed response data if successful.
///
/// # Errors
/// Returns a `MrcError` if the connection to the socket fails or if the response cannot be parsed.
pub async fn send_ipc_command(
command: &str,
args: &[Value],
socket_path: Option<&str>,
) -> Result<Option<Value>> {
let socket_path = socket_path.unwrap_or(SOCKET_PATH);
debug!(
"Sending IPC command: {} with arguments: {:?}",
command, args
);
let mut socket = connect_to_socket(socket_path).await?;
send_message(&mut socket, command, args).await?;
let json_response = read_response(&mut socket).await?;
Ok(json_response.get("data").cloned())
}
/// Represents common MPV commands.
///
/// This enum provides variants for frequently used MPV commands, which can be converted to their
/// string equivalents using the `as_str` method.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
#[derive(Debug)]
pub enum MpvCommand {
/// Sets a property to a specified value in MPV.
SetProperty,
/// Moves to the next item in the playlist.
PlaylistNext,
/// Moves to the previous item in the playlist.
PlaylistPrev,
/// Seeks to a specific time in the current media.
Seek,
/// Quits the MPV application.
Quit,
/// Moves an item in the playlist from one index to another.
PlaylistMove,
/// Removes an item from the playlist.
PlaylistRemove,
/// Clears all items from the playlist.
PlaylistClear,
/// Retrieves the value of a property in MPV.
GetProperty,
/// Loads a file into MPV.
LoadFile,
}
impl MpvCommand {
/// Converts MPV commands to their string equivalents.
///
/// # Returns
/// A string slice representing the command.
#[must_use]
pub const fn as_str(&self) -> &str {
match self {
Self::SetProperty => "set_property",
Self::PlaylistNext => "playlist-next",
Self::PlaylistPrev => "playlist-prev",
Self::Seek => "seek",
Self::Quit => "quit",
Self::PlaylistMove => "playlist-move",
Self::PlaylistRemove => "playlist-remove",
Self::PlaylistClear => "playlist-clear",
Self::GetProperty => "get_property",
Self::LoadFile => "loadfile",
}
}
}
/// Sends the `set_property` command to MPV to change a property value.
///
/// # Arguments
/// - `property`: The name of the property to set.
/// - `value`: The new value to assign to the property.
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn set_property(
property: &str,
value: &Value,
socket_path: Option<&str>,
) -> Result<Option<Value>> {
send_ipc_command(
MpvCommand::SetProperty.as_str(),
&[json!(property), value.clone()],
socket_path,
)
.await
}
/// Sends the `playlist-next` command to move to the next playlist item.
///
/// # Arguments
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn playlist_next(socket_path: Option<&str>) -> Result<Option<Value>> {
send_ipc_command(MpvCommand::PlaylistNext.as_str(), &[], socket_path).await
}
/// Sends the `playlist-prev` command to move to the previous playlist item.
///
/// # Arguments
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn playlist_prev(socket_path: Option<&str>) -> Result<Option<Value>> {
send_ipc_command(MpvCommand::PlaylistPrev.as_str(), &[], socket_path).await
}
/// Sends the `seek` command to seek the media playback by a given number of seconds.
///
/// # Arguments
/// - `seconds`: The number of seconds to seek.
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn seek(seconds: f64, socket_path: Option<&str>) -> Result<Option<Value>> {
send_ipc_command(MpvCommand::Seek.as_str(), &[json!(seconds)], socket_path).await
}
/// Sends the `quit` command to terminate MPV.
///
/// # Arguments
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn quit(socket_path: Option<&str>) -> Result<Option<Value>> {
send_ipc_command(MpvCommand::Quit.as_str(), &[], socket_path).await
}
/// Sends the `playlist-move` command to move a playlist item from one index to another.
///
/// # Arguments
/// - `from_index`: The index of the item to move.
/// - `to_index`: The index to move the item to.
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn playlist_move(
from_index: usize,
to_index: usize,
socket_path: Option<&str>,
) -> Result<Option<Value>> {
send_ipc_command(
MpvCommand::PlaylistMove.as_str(),
&[json!(from_index), json!(to_index)],
socket_path,
)
.await
}
/// Sends the `playlist-remove` command to remove an item from the playlist.
///
/// # Arguments
/// - `index`: The index of the item to remove, or `None` to remove the current item.
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn playlist_remove(
index: Option<usize>,
socket_path: Option<&str>,
) -> Result<Option<Value>> {
let args = index.map_or_else(|| vec![json!("current")], |idx| vec![json!(idx)]);
send_ipc_command(MpvCommand::PlaylistRemove.as_str(), &args, socket_path).await
}
/// Sends the `playlist-clear` command to clear the playlist.
///
/// # Arguments
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn playlist_clear(socket_path: Option<&str>) -> Result<Option<Value>> {
send_ipc_command(MpvCommand::PlaylistClear.as_str(), &[], socket_path).await
}
/// Sends the `get_property` command to retrieve a property value from MPV.
///
/// # Arguments
/// - `property`: The name of the property to retrieve.
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn get_property(property: &str, socket_path: Option<&str>) -> Result<Option<Value>> {
send_ipc_command(
MpvCommand::GetProperty.as_str(),
&[json!(property)],
socket_path,
)
.await
}
/// Sends the `loadfile` command to load a file into MPV.
///
/// # Arguments
/// - `filename`: The name of the file to load.
/// - `append`: Whether to append the file to the playlist (`true`) or replace the current file (`false`).
/// - `socket_path`: An optional custom socket path.
///
/// # Returns
/// A `Result` containing the response data.
///
/// # Errors
/// Returns an error if the connection to the socket fails or the command execution encounters issues.
pub async fn loadfile(
filename: &str,
append: bool,
socket_path: Option<&str>,
) -> Result<Option<Value>> {
let append_flag = if append {
json!("append-play")
} else {
json!("replace")
};
send_ipc_command(
MpvCommand::LoadFile.as_str(),
&[json!(filename), append_flag],
socket_path,
)
.await
}
#[cfg(test)]
mod tests {
use std::error::Error;
use serde_json::json;
use super::*;
#[test]
fn test_mpvrc_error_display() {
let error = MrcError::InvalidInput("test message".to_string());
assert_eq!(error.to_string(), "invalid input: test message");
}
#[test]
fn test_mpvrc_error_from_io_error() {
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let mrc_error = MrcError::from(io_error);
assert!(matches!(mrc_error, MrcError::ConnectionError(_)));
}
#[test]
fn test_mpvrc_error_from_json_error() {
let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
let mrc_error = MrcError::from(json_error);
assert!(matches!(mrc_error, MrcError::ParseError(_)));
}
#[test]
fn test_socket_timeout_error() {
let error = MrcError::SocketTimeout(5);
assert_eq!(
error.to_string(),
"socket operation timed out after 5 seconds"
);
}
#[test]
fn test_mpv_error() {
let error = MrcError::MpvError("playback failed".to_string());
assert_eq!(error.to_string(), "MPV error: playback failed");
}
#[test]
fn test_property_not_found_error() {
let error = MrcError::PropertyNotFound("volume".to_string());
assert_eq!(error.to_string(), "property 'volume' not found");
}
#[test]
fn test_connection_lost_error() {
let error = MrcError::ConnectionLost("socket closed".to_string());
assert_eq!(error.to_string(), "server connection lost: socket closed");
}
#[test]
fn test_tls_error() {
let error = MrcError::TlsError("certificate invalid".to_string());
assert_eq!(error.to_string(), "TLS error: certificate invalid");
}
#[test]
fn test_error_trait_implementation() {
let error = MrcError::InvalidInput("test".to_string());
assert!(error.source().is_none());
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "test");
let connection_error = MrcError::ConnectionError(io_error);
assert!(connection_error.source().is_some());
}
#[test]
fn test_mpv_command_as_str() {
assert_eq!(MpvCommand::SetProperty.as_str(), "set_property");
assert_eq!(MpvCommand::PlaylistNext.as_str(), "playlist-next");
assert_eq!(MpvCommand::PlaylistPrev.as_str(), "playlist-prev");
assert_eq!(MpvCommand::Seek.as_str(), "seek");
assert_eq!(MpvCommand::Quit.as_str(), "quit");
assert_eq!(MpvCommand::PlaylistMove.as_str(), "playlist-move");
assert_eq!(MpvCommand::PlaylistRemove.as_str(), "playlist-remove");
assert_eq!(MpvCommand::PlaylistClear.as_str(), "playlist-clear");
assert_eq!(MpvCommand::GetProperty.as_str(), "get_property");
assert_eq!(MpvCommand::LoadFile.as_str(), "loadfile");
}
#[test]
fn test_mpv_command_debug() {
let cmd = MpvCommand::SetProperty;
let debug_str = format!("{cmd:?}");
assert_eq!(debug_str, "SetProperty");
}
#[test]
fn test_result_type_alias() {
#[allow(clippy::unnecessary_wraps)]
fn test_function() -> Result<String> {
Ok("test".to_string())
}
let result = test_function();
assert!(result.is_ok());
assert_eq!(result.unwrap(), "test");
}
#[test]
fn test_error_variants_exhaustive() {
// Test that all error variants are properly handled
let errors = vec![
MrcError::ConnectionError(std::io::Error::other("test")),
MrcError::ParseError(serde_json::from_str::<serde_json::Value>("").unwrap_err()),
MrcError::SocketTimeout(10),
MrcError::MpvError("test".to_string()),
MrcError::PropertyNotFound("test".to_string()),
MrcError::InvalidUtf8(String::from_utf8(vec![0, 159, 146, 150]).unwrap_err()),
MrcError::NetworkError("test".to_string()),
MrcError::ConnectionLost("test".to_string()),
MrcError::ProtocolError("test".to_string()),
MrcError::InvalidInput("test".to_string()),
MrcError::TlsError("test".to_string()),
];
for error in errors {
// Ensure all errors implement Display
let _ = error.to_string();
// Ensure all errors implement Debug
let _ = format!("{error:?}");
}
}
// Mock tests for functions that would require MPV socket
#[tokio::test]
async fn test_loadfile_append_flag_true() {
// Test that loadfile creates correct append flag for true
// This would fail with socket connection, but tests the parameter handling
let result = loadfile("test.mp4", true, Some("/nonexistent/socket")).await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), MrcError::ConnectionError(_)));
}
#[tokio::test]
async fn test_loadfile_append_flag_false() {
// Test that loadfile creates correct append flag for false
let result = loadfile("test.mp4", false, Some("/nonexistent/socket")).await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), MrcError::ConnectionError(_)));
}
#[tokio::test]
async fn test_seek_parameter_handling() {
let result = seek(42.5, Some("/nonexistent/socket")).await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), MrcError::ConnectionError(_)));
}
#[tokio::test]
async fn test_playlist_move_parameter_handling() {
let result = playlist_move(0, 1, Some("/nonexistent/socket")).await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), MrcError::ConnectionError(_)));
}
#[tokio::test]
async fn test_set_property_parameter_handling() {
let result = set_property("volume", &json!(50), Some("/nonexistent/socket")).await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), MrcError::ConnectionError(_)));
}
#[tokio::test]
async fn test_get_property_parameter_handling() {
let result = get_property("volume", Some("/nonexistent/socket")).await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), MrcError::ConnectionError(_)));
}
#[tokio::test]
async fn test_playlist_operations_error_handling() {
// Test that all playlist operations handle connection errors properly
let socket_path = Some("/nonexistent/socket");
let results = vec![
playlist_next(socket_path).await,
playlist_prev(socket_path).await,
playlist_clear(socket_path).await,
playlist_remove(Some(0), socket_path).await,
playlist_remove(None, socket_path).await,
];
for result in results {
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), MrcError::ConnectionError(_)));
}
}
#[tokio::test]
async fn test_quit_command() {
let result = quit(Some("/nonexistent/socket")).await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), MrcError::ConnectionError(_)));
}
}

357
src/mrc/lib.rs.html Normal file
View file

@ -0,0 +1,357 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/lib.rs`."><title>lib.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../../" data-static-root-path="../../static.files/" data-current-crate="mrc" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../../static.files/storage-82c7156e.js"></script><script defer src="../../static.files/src-script-63605ae7.js"></script><script defer src="../../src-files.js"></script><script defer src="../../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer"></div><main><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1><div class="sub-heading">mrc/</div>lib.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap digits-3"><pre class="rust"><code><a href=#1 id=1 data-nosnippet>1</a><span class="doccomment">//! MRC
<a href=#2 id=2 data-nosnippet>2</a>//! A library for interacting with the MPV media player using its JSON IPC (Inter-Process Communication) protocol.
<a href=#3 id=3 data-nosnippet>3</a>//!
<a href=#4 id=4 data-nosnippet>4</a>//! This crate provides a set of utilities to communicate with MPV's IPC socket, enabling you to send commands
<a href=#5 id=5 data-nosnippet>5</a>//! and retrieve responses in a structured format.
<a href=#6 id=6 data-nosnippet>6</a>//!
<a href=#7 id=7 data-nosnippet>7</a>//! ## Features
<a href=#8 id=8 data-nosnippet>8</a>//!
<a href=#9 id=9 data-nosnippet>9</a>//! - Send commands to MPV's IPC socket
<a href=#10 id=10 data-nosnippet>10</a>//! - Retrieve responses in JSON format
<a href=#11 id=11 data-nosnippet>11</a>//! - Supports common MPV commands like `set_property`, `seek`, and `playlist-next`
<a href=#12 id=12 data-nosnippet>12</a>//! - Flexible socket path configuration
<a href=#13 id=13 data-nosnippet>13</a>//!
<a href=#14 id=14 data-nosnippet>14</a>//! ## Example Usage
<a href=#15 id=15 data-nosnippet>15</a>//! ```rust
<a href=#16 id=16 data-nosnippet>16</a>//! use serde_json::json;
<a href=#17 id=17 data-nosnippet>17</a>//! use tokio;
<a href=#18 id=18 data-nosnippet>18</a>//! use mrc::{send_ipc_command, playlist_next, set_property};
<a href=#19 id=19 data-nosnippet>19</a>//!
<a href=#20 id=20 data-nosnippet>20</a>//! #[tokio::main]
<a href=#21 id=21 data-nosnippet>21</a>//! async fn main() {
<a href=#22 id=22 data-nosnippet>22</a>//! let result = playlist_next(None).await;
<a href=#23 id=23 data-nosnippet>23</a>//! match result {
<a href=#24 id=24 data-nosnippet>24</a>//! Ok(response) =&gt; println!("Playlist moved to next: {:?}", response),
<a href=#25 id=25 data-nosnippet>25</a>//! Err(err) =&gt; eprintln!("Error: {:?}", err),
<a href=#26 id=26 data-nosnippet>26</a>//! }
<a href=#27 id=27 data-nosnippet>27</a>//!
<a href=#28 id=28 data-nosnippet>28</a>//! let property_result = set_property("volume", &amp;json!(50), None).await;
<a href=#29 id=29 data-nosnippet>29</a>//! match property_result {
<a href=#30 id=30 data-nosnippet>30</a>//! Ok(response) =&gt; println!("Volume set: {:?}", response),
<a href=#31 id=31 data-nosnippet>31</a>//! Err(err) =&gt; eprintln!("Error: {:?}", err),
<a href=#32 id=32 data-nosnippet>32</a>//! }
<a href=#33 id=33 data-nosnippet>33</a>//! }
<a href=#34 id=34 data-nosnippet>34</a>//! ```
<a href=#35 id=35 data-nosnippet>35</a>//!
<a href=#36 id=36 data-nosnippet>36</a>//! ## Constants
<a href=#37 id=37 data-nosnippet>37</a>//!
<a href=#38 id=38 data-nosnippet>38</a>//! ### `SOCKET_PATH`
<a href=#39 id=39 data-nosnippet>39</a>//! Default path for the MPV IPC socket: `/tmp/mpvsocket`
<a href=#40 id=40 data-nosnippet>40</a>//!
<a href=#41 id=41 data-nosnippet>41</a>//! ## Functions
<a href=#42 id=42 data-nosnippet>42</a>
<a href=#43 id=43 data-nosnippet>43</a></span><span class="kw">use </span>serde_json::{json, Value};
<a href=#44 id=44 data-nosnippet>44</a><span class="kw">use </span>std::io::{<span class="self">self</span>};
<a href=#45 id=45 data-nosnippet>45</a><span class="kw">use </span>tokio::io::{AsyncReadExt, AsyncWriteExt};
<a href=#46 id=46 data-nosnippet>46</a><span class="kw">use </span>tokio::net::UnixStream;
<a href=#47 id=47 data-nosnippet>47</a><span class="kw">use </span>tracing::{debug, error};
<a href=#48 id=48 data-nosnippet>48</a>
<a href=#49 id=49 data-nosnippet>49</a><span class="kw">pub const </span>SOCKET_PATH: <span class="kw-2">&amp;</span>str = <span class="string">"/tmp/mpvsocket"</span>;
<a href=#50 id=50 data-nosnippet>50</a>
<a href=#51 id=51 data-nosnippet>51</a><span class="doccomment">/// Sends a generic IPC command to the specified socket and returns the parsed response data.
<a href=#52 id=52 data-nosnippet>52</a>///
<a href=#53 id=53 data-nosnippet>53</a>/// # Arguments
<a href=#54 id=54 data-nosnippet>54</a>/// - `command`: The name of the command to send to MPV.
<a href=#55 id=55 data-nosnippet>55</a>/// - `args`: A slice of `Value` arguments to include in the command.
<a href=#56 id=56 data-nosnippet>56</a>/// - `socket_path`: An optional custom path to the MPV IPC socket. If `None`, the default path is used.
<a href=#57 id=57 data-nosnippet>57</a>///
<a href=#58 id=58 data-nosnippet>58</a>/// # Returns
<a href=#59 id=59 data-nosnippet>59</a>/// A `Result` containing an `Option&lt;Value&gt;` with the parsed response data if successful.
<a href=#60 id=60 data-nosnippet>60</a>///
<a href=#61 id=61 data-nosnippet>61</a>/// # Errors
<a href=#62 id=62 data-nosnippet>62</a>/// Returns an error if the connection to the socket fails or if the response cannot be parsed.
<a href=#63 id=63 data-nosnippet>63</a></span><span class="kw">pub async fn </span>send_ipc_command(
<a href=#64 id=64 data-nosnippet>64</a> command: <span class="kw-2">&amp;</span>str,
<a href=#65 id=65 data-nosnippet>65</a> args: <span class="kw-2">&amp;</span>[Value],
<a href=#66 id=66 data-nosnippet>66</a> socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;,
<a href=#67 id=67 data-nosnippet>67</a>) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#68 id=68 data-nosnippet>68</a> <span class="kw">let </span>socket_path = socket_path.unwrap_or(SOCKET_PATH);
<a href=#69 id=69 data-nosnippet>69</a> <span class="macro">debug!</span>(
<a href=#70 id=70 data-nosnippet>70</a> <span class="string">"Sending IPC command: {} with arguments: {:?}"</span>,
<a href=#71 id=71 data-nosnippet>71</a> command, args
<a href=#72 id=72 data-nosnippet>72</a> );
<a href=#73 id=73 data-nosnippet>73</a>
<a href=#74 id=74 data-nosnippet>74</a> <span class="kw">match </span>UnixStream::connect(socket_path).<span class="kw">await </span>{
<a href=#75 id=75 data-nosnippet>75</a> <span class="prelude-val">Ok</span>(<span class="kw-2">mut </span>socket) =&gt; {
<a href=#76 id=76 data-nosnippet>76</a> <span class="macro">debug!</span>(<span class="string">"Connected to socket at {}"</span>, socket_path);
<a href=#77 id=77 data-nosnippet>77</a>
<a href=#78 id=78 data-nosnippet>78</a> <span class="kw">let </span><span class="kw-2">mut </span>command_array = <span class="macro">vec!</span>[<span class="macro">json!</span>(command)];
<a href=#79 id=79 data-nosnippet>79</a> command_array.extend_from_slice(args);
<a href=#80 id=80 data-nosnippet>80</a> <span class="kw">let </span>message = <span class="macro">json!</span>({ <span class="string">"command"</span>: command_array });
<a href=#81 id=81 data-nosnippet>81</a> <span class="kw">let </span>message_str = <span class="macro">format!</span>(<span class="string">"{}\n"</span>, serde_json::to_string(<span class="kw-2">&amp;</span>message)<span class="question-mark">?</span>);
<a href=#82 id=82 data-nosnippet>82</a> <span class="macro">debug!</span>(<span class="string">"Serialized message to send with newline: {}"</span>, message_str);
<a href=#83 id=83 data-nosnippet>83</a>
<a href=#84 id=84 data-nosnippet>84</a> socket.write_all(message_str.as_bytes()).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#85 id=85 data-nosnippet>85</a> socket.flush().<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#86 id=86 data-nosnippet>86</a> <span class="macro">debug!</span>(<span class="string">"Message sent and flushed"</span>);
<a href=#87 id=87 data-nosnippet>87</a>
<a href=#88 id=88 data-nosnippet>88</a> <span class="kw">let </span><span class="kw-2">mut </span>response = <span class="macro">vec!</span>[<span class="number">0</span>; <span class="number">1024</span>];
<a href=#89 id=89 data-nosnippet>89</a> <span class="kw">let </span>n = socket.read(<span class="kw-2">&amp;mut </span>response).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#90 id=90 data-nosnippet>90</a> <span class="kw">let </span>response_str = String::from_utf8_lossy(<span class="kw-2">&amp;</span>response[..n]);
<a href=#91 id=91 data-nosnippet>91</a> <span class="macro">debug!</span>(<span class="string">"Raw response: {}"</span>, response_str);
<a href=#92 id=92 data-nosnippet>92</a>
<a href=#93 id=93 data-nosnippet>93</a> <span class="kw">match </span>serde_json::from_str::&lt;Value&gt;(<span class="kw-2">&amp;</span>response_str) {
<a href=#94 id=94 data-nosnippet>94</a> <span class="prelude-val">Ok</span>(json_response) =&gt; {
<a href=#95 id=95 data-nosnippet>95</a> <span class="macro">debug!</span>(<span class="string">"Parsed IPC response: {:?}"</span>, json_response);
<a href=#96 id=96 data-nosnippet>96</a> <span class="prelude-val">Ok</span>(json_response.get(<span class="string">"data"</span>).cloned())
<a href=#97 id=97 data-nosnippet>97</a> }
<a href=#98 id=98 data-nosnippet>98</a>
<a href=#99 id=99 data-nosnippet>99</a> <span class="prelude-val">Err</span>(e) =&gt; {
<a href=#100 id=100 data-nosnippet>100</a> <span class="macro">error!</span>(<span class="string">"Failed to parse response: {}"</span>, e);
<a href=#101 id=101 data-nosnippet>101</a> <span class="prelude-val">Ok</span>(<span class="prelude-val">None</span>)
<a href=#102 id=102 data-nosnippet>102</a> }
<a href=#103 id=103 data-nosnippet>103</a> }
<a href=#104 id=104 data-nosnippet>104</a> }
<a href=#105 id=105 data-nosnippet>105</a>
<a href=#106 id=106 data-nosnippet>106</a> <span class="prelude-val">Err</span>(e) =&gt; {
<a href=#107 id=107 data-nosnippet>107</a> <span class="macro">error!</span>(<span class="string">"Failed to connect to MPV socket: {}"</span>, e);
<a href=#108 id=108 data-nosnippet>108</a> <span class="prelude-val">Err</span>(e)
<a href=#109 id=109 data-nosnippet>109</a> }
<a href=#110 id=110 data-nosnippet>110</a> }
<a href=#111 id=111 data-nosnippet>111</a>}
<a href=#112 id=112 data-nosnippet>112</a>
<a href=#113 id=113 data-nosnippet>113</a><span class="doccomment">/// Represents common MPV commands.
<a href=#114 id=114 data-nosnippet>114</a>///
<a href=#115 id=115 data-nosnippet>115</a>/// This enum provides variants for frequently used MPV commands, which can be converted to their
<a href=#116 id=116 data-nosnippet>116</a>/// string equivalents using the `as_str` method.
<a href=#117 id=117 data-nosnippet>117</a>///
<a href=#118 id=118 data-nosnippet>118</a>/// # Errors
<a href=#119 id=119 data-nosnippet>119</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#120 id=120 data-nosnippet>120</a></span><span class="attr">#[derive(Debug)]
<a href=#121 id=121 data-nosnippet>121</a></span><span class="kw">pub enum </span>MpvCommand {
<a href=#122 id=122 data-nosnippet>122</a> <span class="doccomment">/// Sets a property to a specified value in MPV.
<a href=#123 id=123 data-nosnippet>123</a> </span>SetProperty,
<a href=#124 id=124 data-nosnippet>124</a> <span class="doccomment">/// Moves to the next item in the playlist.
<a href=#125 id=125 data-nosnippet>125</a> </span>PlaylistNext,
<a href=#126 id=126 data-nosnippet>126</a> <span class="doccomment">/// Moves to the previous item in the playlist.
<a href=#127 id=127 data-nosnippet>127</a> </span>PlaylistPrev,
<a href=#128 id=128 data-nosnippet>128</a> <span class="doccomment">/// Seeks to a specific time in the current media.
<a href=#129 id=129 data-nosnippet>129</a> </span>Seek,
<a href=#130 id=130 data-nosnippet>130</a> <span class="doccomment">/// Quits the MPV application.
<a href=#131 id=131 data-nosnippet>131</a> </span>Quit,
<a href=#132 id=132 data-nosnippet>132</a> <span class="doccomment">/// Moves an item in the playlist from one index to another.
<a href=#133 id=133 data-nosnippet>133</a> </span>PlaylistMove,
<a href=#134 id=134 data-nosnippet>134</a> <span class="doccomment">/// Removes an item from the playlist.
<a href=#135 id=135 data-nosnippet>135</a> </span>PlaylistRemove,
<a href=#136 id=136 data-nosnippet>136</a> <span class="doccomment">/// Clears all items from the playlist.
<a href=#137 id=137 data-nosnippet>137</a> </span>PlaylistClear,
<a href=#138 id=138 data-nosnippet>138</a> <span class="doccomment">/// Retrieves the value of a property in MPV.
<a href=#139 id=139 data-nosnippet>139</a> </span>GetProperty,
<a href=#140 id=140 data-nosnippet>140</a> <span class="doccomment">/// Loads a file into MPV.
<a href=#141 id=141 data-nosnippet>141</a> </span>LoadFile,
<a href=#142 id=142 data-nosnippet>142</a>}
<a href=#143 id=143 data-nosnippet>143</a>
<a href=#144 id=144 data-nosnippet>144</a><span class="kw">impl </span>MpvCommand {
<a href=#145 id=145 data-nosnippet>145</a> <span class="doccomment">/// Converts MPV commands to their string equivalents.
<a href=#146 id=146 data-nosnippet>146</a> ///
<a href=#147 id=147 data-nosnippet>147</a> /// # Returns
<a href=#148 id=148 data-nosnippet>148</a> /// A string slice representing the command.
<a href=#149 id=149 data-nosnippet>149</a> </span><span class="attr">#[must_use]
<a href=#150 id=150 data-nosnippet>150</a> </span><span class="kw">pub const fn </span>as_str(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="kw-2">&amp;</span>str {
<a href=#151 id=151 data-nosnippet>151</a> <span class="kw">match </span><span class="self">self </span>{
<a href=#152 id=152 data-nosnippet>152</a> <span class="self">Self</span>::SetProperty =&gt; <span class="string">"set_property"</span>,
<a href=#153 id=153 data-nosnippet>153</a> <span class="self">Self</span>::PlaylistNext =&gt; <span class="string">"playlist-next"</span>,
<a href=#154 id=154 data-nosnippet>154</a> <span class="self">Self</span>::PlaylistPrev =&gt; <span class="string">"playlist-prev"</span>,
<a href=#155 id=155 data-nosnippet>155</a> <span class="self">Self</span>::Seek =&gt; <span class="string">"seek"</span>,
<a href=#156 id=156 data-nosnippet>156</a> <span class="self">Self</span>::Quit =&gt; <span class="string">"quit"</span>,
<a href=#157 id=157 data-nosnippet>157</a> <span class="self">Self</span>::PlaylistMove =&gt; <span class="string">"playlist-move"</span>,
<a href=#158 id=158 data-nosnippet>158</a> <span class="self">Self</span>::PlaylistRemove =&gt; <span class="string">"playlist-remove"</span>,
<a href=#159 id=159 data-nosnippet>159</a> <span class="self">Self</span>::PlaylistClear =&gt; <span class="string">"playlist-clear"</span>,
<a href=#160 id=160 data-nosnippet>160</a> <span class="self">Self</span>::GetProperty =&gt; <span class="string">"get_property"</span>,
<a href=#161 id=161 data-nosnippet>161</a> <span class="self">Self</span>::LoadFile =&gt; <span class="string">"loadfile"</span>,
<a href=#162 id=162 data-nosnippet>162</a> }
<a href=#163 id=163 data-nosnippet>163</a> }
<a href=#164 id=164 data-nosnippet>164</a>}
<a href=#165 id=165 data-nosnippet>165</a>
<a href=#166 id=166 data-nosnippet>166</a><span class="doccomment">/// Sends the `set_property` command to MPV to change a property value.
<a href=#167 id=167 data-nosnippet>167</a>///
<a href=#168 id=168 data-nosnippet>168</a>/// # Arguments
<a href=#169 id=169 data-nosnippet>169</a>/// - `property`: The name of the property to set.
<a href=#170 id=170 data-nosnippet>170</a>/// - `value`: The new value to assign to the property.
<a href=#171 id=171 data-nosnippet>171</a>/// - `socket_path`: An optional custom socket path.
<a href=#172 id=172 data-nosnippet>172</a>///
<a href=#173 id=173 data-nosnippet>173</a>/// # Returns
<a href=#174 id=174 data-nosnippet>174</a>/// A `Result` containing the response data.
<a href=#175 id=175 data-nosnippet>175</a>///
<a href=#176 id=176 data-nosnippet>176</a>/// # Errors
<a href=#177 id=177 data-nosnippet>177</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#178 id=178 data-nosnippet>178</a></span><span class="kw">pub async fn </span>set_property(
<a href=#179 id=179 data-nosnippet>179</a> property: <span class="kw-2">&amp;</span>str,
<a href=#180 id=180 data-nosnippet>180</a> value: <span class="kw-2">&amp;</span>Value,
<a href=#181 id=181 data-nosnippet>181</a> socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;,
<a href=#182 id=182 data-nosnippet>182</a>) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#183 id=183 data-nosnippet>183</a> send_ipc_command(
<a href=#184 id=184 data-nosnippet>184</a> MpvCommand::SetProperty.as_str(),
<a href=#185 id=185 data-nosnippet>185</a> <span class="kw-2">&amp;</span>[<span class="macro">json!</span>(property), value.clone()],
<a href=#186 id=186 data-nosnippet>186</a> socket_path,
<a href=#187 id=187 data-nosnippet>187</a> )
<a href=#188 id=188 data-nosnippet>188</a> .<span class="kw">await
<a href=#189 id=189 data-nosnippet>189</a></span>}
<a href=#190 id=190 data-nosnippet>190</a>
<a href=#191 id=191 data-nosnippet>191</a><span class="doccomment">/// Sends the `playlist-next` command to move to the next playlist item.
<a href=#192 id=192 data-nosnippet>192</a>///
<a href=#193 id=193 data-nosnippet>193</a>/// # Arguments
<a href=#194 id=194 data-nosnippet>194</a>/// - `socket_path`: An optional custom socket path.
<a href=#195 id=195 data-nosnippet>195</a>///
<a href=#196 id=196 data-nosnippet>196</a>/// # Returns
<a href=#197 id=197 data-nosnippet>197</a>/// A `Result` containing the response data.
<a href=#198 id=198 data-nosnippet>198</a>///
<a href=#199 id=199 data-nosnippet>199</a>/// # Errors
<a href=#200 id=200 data-nosnippet>200</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#201 id=201 data-nosnippet>201</a></span><span class="kw">pub async fn </span>playlist_next(socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#202 id=202 data-nosnippet>202</a> send_ipc_command(MpvCommand::PlaylistNext.as_str(), <span class="kw-2">&amp;</span>[], socket_path).<span class="kw">await
<a href=#203 id=203 data-nosnippet>203</a></span>}
<a href=#204 id=204 data-nosnippet>204</a>
<a href=#205 id=205 data-nosnippet>205</a><span class="doccomment">/// Sends the `playlist-prev` command to move to the previous playlist item.
<a href=#206 id=206 data-nosnippet>206</a>///
<a href=#207 id=207 data-nosnippet>207</a>/// # Arguments
<a href=#208 id=208 data-nosnippet>208</a>/// - `socket_path`: An optional custom socket path.
<a href=#209 id=209 data-nosnippet>209</a>///
<a href=#210 id=210 data-nosnippet>210</a>/// # Returns
<a href=#211 id=211 data-nosnippet>211</a>/// A `Result` containing the response data.
<a href=#212 id=212 data-nosnippet>212</a>///
<a href=#213 id=213 data-nosnippet>213</a>/// # Errors
<a href=#214 id=214 data-nosnippet>214</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#215 id=215 data-nosnippet>215</a></span><span class="kw">pub async fn </span>playlist_prev(socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#216 id=216 data-nosnippet>216</a> send_ipc_command(MpvCommand::PlaylistPrev.as_str(), <span class="kw-2">&amp;</span>[], socket_path).<span class="kw">await
<a href=#217 id=217 data-nosnippet>217</a></span>}
<a href=#218 id=218 data-nosnippet>218</a>
<a href=#219 id=219 data-nosnippet>219</a><span class="doccomment">/// Sends the `seek` command to seek the media playback by a given number of seconds.
<a href=#220 id=220 data-nosnippet>220</a>///
<a href=#221 id=221 data-nosnippet>221</a>/// # Arguments
<a href=#222 id=222 data-nosnippet>222</a>/// - `seconds`: The number of seconds to seek.
<a href=#223 id=223 data-nosnippet>223</a>/// - `socket_path`: An optional custom socket path.
<a href=#224 id=224 data-nosnippet>224</a>///
<a href=#225 id=225 data-nosnippet>225</a>/// # Returns
<a href=#226 id=226 data-nosnippet>226</a>/// A `Result` containing the response data.
<a href=#227 id=227 data-nosnippet>227</a>///
<a href=#228 id=228 data-nosnippet>228</a>/// # Errors
<a href=#229 id=229 data-nosnippet>229</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#230 id=230 data-nosnippet>230</a></span><span class="kw">pub async fn </span>seek(seconds: f64, socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#231 id=231 data-nosnippet>231</a> send_ipc_command(MpvCommand::Seek.as_str(), <span class="kw-2">&amp;</span>[<span class="macro">json!</span>(seconds)], socket_path).<span class="kw">await
<a href=#232 id=232 data-nosnippet>232</a></span>}
<a href=#233 id=233 data-nosnippet>233</a>
<a href=#234 id=234 data-nosnippet>234</a><span class="doccomment">/// Sends the `quit` command to terminate MPV.
<a href=#235 id=235 data-nosnippet>235</a>///
<a href=#236 id=236 data-nosnippet>236</a>/// # Arguments
<a href=#237 id=237 data-nosnippet>237</a>/// - `socket_path`: An optional custom socket path.
<a href=#238 id=238 data-nosnippet>238</a>///
<a href=#239 id=239 data-nosnippet>239</a>/// # Returns
<a href=#240 id=240 data-nosnippet>240</a>/// A `Result` containing the response data.
<a href=#241 id=241 data-nosnippet>241</a>///
<a href=#242 id=242 data-nosnippet>242</a>/// # Errors
<a href=#243 id=243 data-nosnippet>243</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#244 id=244 data-nosnippet>244</a></span><span class="kw">pub async fn </span>quit(socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#245 id=245 data-nosnippet>245</a> send_ipc_command(MpvCommand::Quit.as_str(), <span class="kw-2">&amp;</span>[], socket_path).<span class="kw">await
<a href=#246 id=246 data-nosnippet>246</a></span>}
<a href=#247 id=247 data-nosnippet>247</a>
<a href=#248 id=248 data-nosnippet>248</a><span class="doccomment">/// Sends the `playlist-move` command to move a playlist item from one index to another.
<a href=#249 id=249 data-nosnippet>249</a>///
<a href=#250 id=250 data-nosnippet>250</a>/// # Arguments
<a href=#251 id=251 data-nosnippet>251</a>/// - `from_index`: The index of the item to move.
<a href=#252 id=252 data-nosnippet>252</a>/// - `to_index`: The index to move the item to.
<a href=#253 id=253 data-nosnippet>253</a>/// - `socket_path`: An optional custom socket path.
<a href=#254 id=254 data-nosnippet>254</a>///
<a href=#255 id=255 data-nosnippet>255</a>/// # Returns
<a href=#256 id=256 data-nosnippet>256</a>/// A `Result` containing the response data.
<a href=#257 id=257 data-nosnippet>257</a>///
<a href=#258 id=258 data-nosnippet>258</a>/// # Errors
<a href=#259 id=259 data-nosnippet>259</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#260 id=260 data-nosnippet>260</a></span><span class="kw">pub async fn </span>playlist_move(
<a href=#261 id=261 data-nosnippet>261</a> from_index: usize,
<a href=#262 id=262 data-nosnippet>262</a> to_index: usize,
<a href=#263 id=263 data-nosnippet>263</a> socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;,
<a href=#264 id=264 data-nosnippet>264</a>) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#265 id=265 data-nosnippet>265</a> send_ipc_command(
<a href=#266 id=266 data-nosnippet>266</a> MpvCommand::PlaylistMove.as_str(),
<a href=#267 id=267 data-nosnippet>267</a> <span class="kw-2">&amp;</span>[<span class="macro">json!</span>(from_index), <span class="macro">json!</span>(to_index)],
<a href=#268 id=268 data-nosnippet>268</a> socket_path,
<a href=#269 id=269 data-nosnippet>269</a> )
<a href=#270 id=270 data-nosnippet>270</a> .<span class="kw">await
<a href=#271 id=271 data-nosnippet>271</a></span>}
<a href=#272 id=272 data-nosnippet>272</a>
<a href=#273 id=273 data-nosnippet>273</a><span class="doccomment">/// Sends the `playlist-remove` command to remove an item from the playlist.
<a href=#274 id=274 data-nosnippet>274</a>///
<a href=#275 id=275 data-nosnippet>275</a>/// # Arguments
<a href=#276 id=276 data-nosnippet>276</a>/// - `index`: The index of the item to remove, or `None` to remove the current item.
<a href=#277 id=277 data-nosnippet>277</a>/// - `socket_path`: An optional custom socket path.
<a href=#278 id=278 data-nosnippet>278</a>///
<a href=#279 id=279 data-nosnippet>279</a>/// # Returns
<a href=#280 id=280 data-nosnippet>280</a>/// A `Result` containing the response data.
<a href=#281 id=281 data-nosnippet>281</a>///
<a href=#282 id=282 data-nosnippet>282</a>/// # Errors
<a href=#283 id=283 data-nosnippet>283</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#284 id=284 data-nosnippet>284</a></span><span class="kw">pub async fn </span>playlist_remove(
<a href=#285 id=285 data-nosnippet>285</a> index: <span class="prelude-ty">Option</span>&lt;usize&gt;,
<a href=#286 id=286 data-nosnippet>286</a> socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;,
<a href=#287 id=287 data-nosnippet>287</a>) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#288 id=288 data-nosnippet>288</a> <span class="kw">let </span>args = <span class="kw">match </span>index {
<a href=#289 id=289 data-nosnippet>289</a> <span class="prelude-val">Some</span>(idx) =&gt; <span class="macro">vec!</span>[<span class="macro">json!</span>(idx)],
<a href=#290 id=290 data-nosnippet>290</a> <span class="prelude-val">None </span>=&gt; <span class="macro">vec!</span>[<span class="macro">json!</span>(<span class="string">"current"</span>)],
<a href=#291 id=291 data-nosnippet>291</a> };
<a href=#292 id=292 data-nosnippet>292</a> send_ipc_command(MpvCommand::PlaylistRemove.as_str(), <span class="kw-2">&amp;</span>args, socket_path).<span class="kw">await
<a href=#293 id=293 data-nosnippet>293</a></span>}
<a href=#294 id=294 data-nosnippet>294</a>
<a href=#295 id=295 data-nosnippet>295</a><span class="doccomment">/// Sends the `playlist-clear` command to clear the playlist.
<a href=#296 id=296 data-nosnippet>296</a>///
<a href=#297 id=297 data-nosnippet>297</a>/// # Arguments
<a href=#298 id=298 data-nosnippet>298</a>/// - `socket_path`: An optional custom socket path.
<a href=#299 id=299 data-nosnippet>299</a>///
<a href=#300 id=300 data-nosnippet>300</a>/// # Returns
<a href=#301 id=301 data-nosnippet>301</a>/// A `Result` containing the response data.
<a href=#302 id=302 data-nosnippet>302</a>///
<a href=#303 id=303 data-nosnippet>303</a>/// # Errors
<a href=#304 id=304 data-nosnippet>304</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#305 id=305 data-nosnippet>305</a></span><span class="kw">pub async fn </span>playlist_clear(socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#306 id=306 data-nosnippet>306</a> send_ipc_command(MpvCommand::PlaylistClear.as_str(), <span class="kw-2">&amp;</span>[], socket_path).<span class="kw">await
<a href=#307 id=307 data-nosnippet>307</a></span>}
<a href=#308 id=308 data-nosnippet>308</a>
<a href=#309 id=309 data-nosnippet>309</a><span class="doccomment">/// Sends the `get_property` command to retrieve a property value from MPV.
<a href=#310 id=310 data-nosnippet>310</a>///
<a href=#311 id=311 data-nosnippet>311</a>/// # Arguments
<a href=#312 id=312 data-nosnippet>312</a>/// - `property`: The name of the property to retrieve.
<a href=#313 id=313 data-nosnippet>313</a>/// - `socket_path`: An optional custom socket path.
<a href=#314 id=314 data-nosnippet>314</a>///
<a href=#315 id=315 data-nosnippet>315</a>/// # Returns
<a href=#316 id=316 data-nosnippet>316</a>/// A `Result` containing the response data.
<a href=#317 id=317 data-nosnippet>317</a>///
<a href=#318 id=318 data-nosnippet>318</a>/// # Errors
<a href=#319 id=319 data-nosnippet>319</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#320 id=320 data-nosnippet>320</a></span><span class="kw">pub async fn </span>get_property(property: <span class="kw-2">&amp;</span>str, socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#321 id=321 data-nosnippet>321</a> send_ipc_command(
<a href=#322 id=322 data-nosnippet>322</a> MpvCommand::GetProperty.as_str(),
<a href=#323 id=323 data-nosnippet>323</a> <span class="kw-2">&amp;</span>[<span class="macro">json!</span>(property)],
<a href=#324 id=324 data-nosnippet>324</a> socket_path,
<a href=#325 id=325 data-nosnippet>325</a> )
<a href=#326 id=326 data-nosnippet>326</a> .<span class="kw">await
<a href=#327 id=327 data-nosnippet>327</a></span>}
<a href=#328 id=328 data-nosnippet>328</a>
<a href=#329 id=329 data-nosnippet>329</a><span class="doccomment">/// Sends the `loadfile` command to load a file into MPV.
<a href=#330 id=330 data-nosnippet>330</a>///
<a href=#331 id=331 data-nosnippet>331</a>/// # Arguments
<a href=#332 id=332 data-nosnippet>332</a>/// - `filename`: The name of the file to load.
<a href=#333 id=333 data-nosnippet>333</a>/// - `append`: Whether to append the file to the playlist (`true`) or replace the current file (`false`).
<a href=#334 id=334 data-nosnippet>334</a>/// - `socket_path`: An optional custom socket path.
<a href=#335 id=335 data-nosnippet>335</a>///
<a href=#336 id=336 data-nosnippet>336</a>/// # Returns
<a href=#337 id=337 data-nosnippet>337</a>/// A `Result` containing the response data.
<a href=#338 id=338 data-nosnippet>338</a>///
<a href=#339 id=339 data-nosnippet>339</a>/// # Errors
<a href=#340 id=340 data-nosnippet>340</a>/// Returns an error if the connection to the socket fails or the command execution encounters issues.
<a href=#341 id=341 data-nosnippet>341</a></span><span class="kw">pub async fn </span>loadfile(
<a href=#342 id=342 data-nosnippet>342</a> filename: <span class="kw-2">&amp;</span>str,
<a href=#343 id=343 data-nosnippet>343</a> append: bool,
<a href=#344 id=344 data-nosnippet>344</a> socket_path: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt;,
<a href=#345 id=345 data-nosnippet>345</a>) -&gt; io::Result&lt;<span class="prelude-ty">Option</span>&lt;Value&gt;&gt; {
<a href=#346 id=346 data-nosnippet>346</a> <span class="kw">let </span>append_flag = <span class="kw">if </span>append {
<a href=#347 id=347 data-nosnippet>347</a> <span class="macro">json!</span>(<span class="string">"append-play"</span>)
<a href=#348 id=348 data-nosnippet>348</a> } <span class="kw">else </span>{
<a href=#349 id=349 data-nosnippet>349</a> <span class="macro">json!</span>(<span class="string">"replace"</span>)
<a href=#350 id=350 data-nosnippet>350</a> };
<a href=#351 id=351 data-nosnippet>351</a> send_ipc_command(
<a href=#352 id=352 data-nosnippet>352</a> MpvCommand::LoadFile.as_str(),
<a href=#353 id=353 data-nosnippet>353</a> <span class="kw-2">&amp;</span>[<span class="macro">json!</span>(filename), append_flag],
<a href=#354 id=354 data-nosnippet>354</a> socket_path,
<a href=#355 id=355 data-nosnippet>355</a> )
<a href=#356 id=356 data-nosnippet>356</a> .<span class="kw">await
<a href=#357 id=357 data-nosnippet>357</a></span>}</code></pre></div></section></main></body></html>

View file

@ -1,240 +0,0 @@
use std::{env, io::Read, sync::Arc};
use clap::Parser;
use mpvrc::{MrcError, Result as MrcResult, SOCKET_PATH, commands::Commands};
use native_tls::{Identity, TlsAcceptor as NativeTlsAcceptor};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio_native_tls::TlsAcceptor;
use tracing::{debug, error, info, warn};
#[derive(Parser)]
#[command(author, version, about)]
struct Config {
/// The IP address and port to bind the server to
#[arg(short, long, default_value = "127.0.0.1:8080")]
bind: String,
/// Path to MPV IPC socket
#[arg(short, long)]
socket: Option<String>,
}
impl Config {
fn socket_path(&self) -> String {
self.socket
.clone()
.unwrap_or_else(|| SOCKET_PATH.to_string())
}
}
async fn handle_connection(
stream: tokio::net::TcpStream,
acceptor: Arc<TlsAcceptor>,
socket_path: String,
) -> MrcResult<()> {
let mut stream = acceptor
.accept(stream)
.await
.map_err(|e| MrcError::TlsError(e.to_string()))?;
let mut buffer = vec![0; 2048];
let n = stream
.read(&mut buffer)
.await
.map_err(MrcError::ConnectionError)?;
let request = String::from_utf8_lossy(&buffer[..n]);
debug!("Received request:\n{}", request);
let headers = request.split("\r\n").collect::<Vec<&str>>();
let token_line = headers
.iter()
.find(|&&line| line.starts_with("Authorization:"));
let token = token_line.map_or("", |line| line.split(' ').nth(1).unwrap_or_default());
let Ok(auth_token) = env::var("AUTH_TOKEN") else {
warn!("AUTH_TOKEN environment variable not set. Authentication disabled.");
let response =
"HTTP/1.1 401 Unauthorized\r\nContent-Length: 29\r\n\r\nAuthentication token not set\n";
stream.write_all(response.as_bytes()).await?;
return Ok(());
};
if token.is_empty() || token != auth_token {
warn!(
"Authentication failed for token: {}",
if token.is_empty() {
"<empty>"
} else {
"<redacted>"
}
);
let response =
"HTTP/1.1 401 Unauthorized\r\nContent-Length: 21\r\n\r\nAuthentication failed\n";
stream.write_all(response.as_bytes()).await?;
return Ok(());
}
info!("Client authenticated successfully");
let command = request.split("\r\n\r\n").last().unwrap_or("").trim();
if command.is_empty() {
warn!("Received empty command");
let response =
"HTTP/1.1 400 Bad Request\r\nContent-Length: 20\r\n\r\nNo command provided\n";
stream.write_all(response.as_bytes()).await?;
return Ok(());
}
info!("Processing command: {}", command);
let (status_code, response_body) = match process_command(command, &socket_path).await {
Ok(response) => ("200 OK", response),
Err(e) => {
error!("Error processing command '{}': {}", command, e);
("400 Bad Request", format!("Error: {e}\n"))
}
};
let http_response = format!(
"HTTP/1.1 {}\r\nContent-Length: {}\r\nContent-Type: text/plain\r\n\r\n{}",
status_code,
response_body.len(),
response_body
);
stream.write_all(http_response.as_bytes()).await?;
Ok(())
}
async fn process_command(command: &str, socket_path: &str) -> MrcResult<String> {
let parts: Vec<&str> = command.split_whitespace().collect();
match parts.as_slice() {
["pause"] => {
Commands::pause(Some(socket_path)).await?;
Ok("Paused playback\n".to_string())
}
["play"] => {
Commands::play(None, Some(socket_path)).await?;
Ok("Resumed playback\n".to_string())
}
["play", index] => {
if let Ok(idx) = index.parse::<usize>() {
Commands::play(Some(idx), Some(socket_path)).await?;
Ok(format!("Playing from index {idx}\n"))
} else {
Err(MrcError::InvalidInput(format!("Invalid index: {index}")))
}
}
["stop"] => {
Commands::stop(Some(socket_path)).await?;
Ok("Stopped playback\n".to_string())
}
["next"] => {
Commands::next(Some(socket_path)).await?;
Ok("Skipped to next item\n".to_string())
}
["prev"] => {
Commands::prev(Some(socket_path)).await?;
Ok("Skipped to previous item\n".to_string())
}
["seek", seconds] => {
if let Ok(sec) = seconds.parse::<f64>() {
Commands::seek_to(sec, Some(socket_path)).await?;
Ok(format!("Seeking to {sec} seconds\n"))
} else {
Err(MrcError::InvalidInput(format!(
"Invalid seconds: {seconds}"
)))
}
}
["clear"] => {
Commands::clear_playlist(Some(socket_path)).await?;
Ok("Cleared playlist\n".to_string())
}
["list"] => match mpvrc::get_property("playlist", Some(socket_path)).await? {
Some(data) => {
let pretty_json =
serde_json::to_string_pretty(&data).map_err(MrcError::ParseError)?;
Ok(format!("Playlist: {pretty_json}\n"))
}
None => Ok("Playlist is empty\n".to_string()),
},
_ => Err(MrcError::InvalidInput(format!(
"Unknown command: {}. Available commands: pause, play [index], stop, next, prev, seek <seconds>, clear, list",
command.trim()
))),
}
}
fn create_tls_acceptor() -> MrcResult<TlsAcceptor> {
let pfx_path = env::var("TLS_PFX_PATH")
.map_err(|_| MrcError::InvalidInput("TLS_PFX_PATH not set".to_string()))?;
let password = env::var("TLS_PASSWORD")
.map_err(|_| MrcError::InvalidInput("TLS_PASSWORD not set".to_string()))?;
let mut file = std::fs::File::open(&pfx_path).map_err(MrcError::ConnectionError)?;
let mut identity = vec![];
file.read_to_end(&mut identity)
.map_err(MrcError::ConnectionError)?;
let identity = Identity::from_pkcs12(&identity, &password)
.map_err(|e| MrcError::TlsError(e.to_string()))?;
let native_acceptor =
NativeTlsAcceptor::new(identity).map_err(|e| MrcError::TlsError(e.to_string()))?;
Ok(TlsAcceptor::from(native_acceptor))
}
#[tokio::main]
async fn main() -> MrcResult<()> {
tracing_subscriber::fmt::init();
let config = Config::parse();
let socket_path = config.socket_path();
if !std::path::Path::new(&socket_path).exists() {
error!(
"Error: MPV socket not found at '{}'. Is MPV running?",
socket_path
);
return Err(MrcError::ConnectionError(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("MPV socket not found at '{socket_path}'"),
)));
}
info!("Server is starting...");
match create_tls_acceptor() {
Ok(acceptor) => {
let acceptor = Arc::new(acceptor);
let listener = tokio::net::TcpListener::bind(&config.bind)
.await
.map_err(MrcError::ConnectionError)?;
info!("Server is listening on {}", config.bind);
loop {
let (stream, _) = listener.accept().await.map_err(MrcError::ConnectionError)?;
info!("New connection accepted.");
let acceptor = Arc::clone(&acceptor);
tokio::spawn(handle_connection(stream, acceptor, socket_path.clone()));
}
}
Err(e) => {
error!("Failed to initialize TLS: {}", e);
return Err(e);
}
}
}

217
src/server/server.rs.html Normal file
View file

@ -0,0 +1,217 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/server.rs`."><title>server.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../static.files/rustdoc-916cea96.css"><meta name="rustdoc-vars" data-root-path="../../" data-static-root-path="../../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.87.0 (17067e9ac 2025-05-09)" data-channel="1.87.0" data-search-js="search-e7298875.js" data-settings-js="settings-d72f25bb.js" ><script src="../../static.files/storage-82c7156e.js"></script><script defer src="../../static.files/src-script-63605ae7.js"></script><script defer src="../../src-files.js"></script><script defer src="../../static.files/main-fb8c74a8.js"></script><noscript><link rel="stylesheet" href="../../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer"></div><main><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1><div class="sub-heading">server/</div>server.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap digits-3"><pre class="rust"><code><a href=#1 id=1 data-nosnippet>1</a><span class="kw">use </span>std::env;
<a href=#2 id=2 data-nosnippet>2</a><span class="kw">use </span>std::io::Read;
<a href=#3 id=3 data-nosnippet>3</a><span class="kw">use </span>std::sync::Arc;
<a href=#4 id=4 data-nosnippet>4</a>
<a href=#5 id=5 data-nosnippet>5</a><span class="kw">use </span>clap::Parser;
<a href=#6 id=6 data-nosnippet>6</a><span class="kw">use </span>native_tls::{Identity, TlsAcceptor <span class="kw">as </span>NativeTlsAcceptor};
<a href=#7 id=7 data-nosnippet>7</a><span class="kw">use </span>serde_json::json;
<a href=#8 id=8 data-nosnippet>8</a><span class="kw">use </span>tokio::io::{AsyncReadExt, AsyncWriteExt};
<a href=#9 id=9 data-nosnippet>9</a><span class="kw">use </span>tokio_native_tls::TlsAcceptor;
<a href=#10 id=10 data-nosnippet>10</a><span class="kw">use </span>tracing::{debug, error, info};
<a href=#11 id=11 data-nosnippet>11</a>
<a href=#12 id=12 data-nosnippet>12</a><span class="kw">use </span>mrc::{get_property, playlist_clear, playlist_next, playlist_prev, quit, seek, set_property};
<a href=#13 id=13 data-nosnippet>13</a>
<a href=#14 id=14 data-nosnippet>14</a><span class="attr">#[derive(Parser)]
<a href=#15 id=15 data-nosnippet>15</a>#[command(author, version, about)]
<a href=#16 id=16 data-nosnippet>16</a></span><span class="kw">struct </span>Config {
<a href=#17 id=17 data-nosnippet>17</a> <span class="doccomment">/// The IP address and port to bind the server to
<a href=#18 id=18 data-nosnippet>18</a> </span><span class="attr">#[arg(short, long, default_value = <span class="string">"127.0.0.1:8080"</span>)]
<a href=#19 id=19 data-nosnippet>19</a> </span>bind: String,
<a href=#20 id=20 data-nosnippet>20</a>
<a href=#21 id=21 data-nosnippet>21</a> <span class="doccomment">/// Path to MPV IPC socket
<a href=#22 id=22 data-nosnippet>22</a> </span><span class="attr">#[arg(short, long, default_value = <span class="string">"/tmp/mpvsocket"</span>)]
<a href=#23 id=23 data-nosnippet>23</a> </span>socket: String,
<a href=#24 id=24 data-nosnippet>24</a>}
<a href=#25 id=25 data-nosnippet>25</a>
<a href=#26 id=26 data-nosnippet>26</a><span class="kw">async fn </span>handle_connection(
<a href=#27 id=27 data-nosnippet>27</a> stream: tokio::net::TcpStream,
<a href=#28 id=28 data-nosnippet>28</a> acceptor: Arc&lt;TlsAcceptor&gt;,
<a href=#29 id=29 data-nosnippet>29</a>) -&gt; <span class="prelude-ty">Result</span>&lt;(), Box&lt;<span class="kw">dyn </span>std::error::Error + Send + Sync&gt;&gt; {
<a href=#30 id=30 data-nosnippet>30</a> <span class="kw">let </span><span class="kw-2">mut </span>stream = acceptor.accept(stream).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#31 id=31 data-nosnippet>31</a> <span class="kw">let </span><span class="kw-2">mut </span>buffer = <span class="macro">vec!</span>[<span class="number">0</span>; <span class="number">2048</span>];
<a href=#32 id=32 data-nosnippet>32</a>
<a href=#33 id=33 data-nosnippet>33</a> <span class="kw">let </span>n = stream.read(<span class="kw-2">&amp;mut </span>buffer).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#34 id=34 data-nosnippet>34</a> <span class="kw">let </span>request = String::from_utf8_lossy(<span class="kw-2">&amp;</span>buffer[..n]);
<a href=#35 id=35 data-nosnippet>35</a>
<a href=#36 id=36 data-nosnippet>36</a> <span class="macro">debug!</span>(<span class="string">"Received request:\n{}"</span>, request);
<a href=#37 id=37 data-nosnippet>37</a>
<a href=#38 id=38 data-nosnippet>38</a> <span class="kw">let </span>headers = request.split(<span class="string">"\r\n"</span>).collect::&lt;Vec&lt;<span class="kw-2">&amp;</span>str&gt;&gt;();
<a href=#39 id=39 data-nosnippet>39</a> <span class="kw">let </span>token_line = headers
<a href=#40 id=40 data-nosnippet>40</a> .iter()
<a href=#41 id=41 data-nosnippet>41</a> .find(|&amp;&amp;line| line.starts_with(<span class="string">"Authorization:"</span>));
<a href=#42 id=42 data-nosnippet>42</a> <span class="kw">let </span>token = <span class="kw">match </span>token_line {
<a href=#43 id=43 data-nosnippet>43</a> <span class="prelude-val">Some</span>(line) =&gt; line.split(<span class="string">" "</span>).nth(<span class="number">1</span>).unwrap_or_default(),
<a href=#44 id=44 data-nosnippet>44</a> <span class="prelude-val">None </span>=&gt; <span class="string">""</span>,
<a href=#45 id=45 data-nosnippet>45</a> };
<a href=#46 id=46 data-nosnippet>46</a>
<a href=#47 id=47 data-nosnippet>47</a> <span class="kw">let </span>auth_token = <span class="kw">match </span>env::var(<span class="string">"AUTH_TOKEN"</span>) {
<a href=#48 id=48 data-nosnippet>48</a> <span class="prelude-val">Ok</span>(token) =&gt; token,
<a href=#49 id=49 data-nosnippet>49</a> <span class="prelude-val">Err</span>(<span class="kw">_</span>) =&gt; {
<a href=#50 id=50 data-nosnippet>50</a> <span class="macro">error!</span>(<span class="string">"Authentication token is not set. Connection cannot be accepted."</span>);
<a href=#51 id=51 data-nosnippet>51</a> stream.write_all(<span class="string">b"Authentication token not set\n"</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#52 id=52 data-nosnippet>52</a>
<a href=#53 id=53 data-nosnippet>53</a> <span class="comment">// You know what? I do not care to panic when the authentication token is
<a href=#54 id=54 data-nosnippet>54</a> // missing in the environment. Start the goddamned server and hell, even
<a href=#55 id=55 data-nosnippet>55</a> // accept incoming connections. Authenticated requests will be refused
<a href=#56 id=56 data-nosnippet>56</a> // when the token is incorrect or not set, so we can simply continue here.
<a href=#57 id=57 data-nosnippet>57</a> </span><span class="kw">return </span><span class="prelude-val">Ok</span>(());
<a href=#58 id=58 data-nosnippet>58</a> }
<a href=#59 id=59 data-nosnippet>59</a> };
<a href=#60 id=60 data-nosnippet>60</a>
<a href=#61 id=61 data-nosnippet>61</a> <span class="kw">if </span>token != auth_token {
<a href=#62 id=62 data-nosnippet>62</a> stream.write_all(<span class="string">b"Authentication failed\n"</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#63 id=63 data-nosnippet>63</a> <span class="kw">return </span><span class="prelude-val">Ok</span>(());
<a href=#64 id=64 data-nosnippet>64</a> }
<a href=#65 id=65 data-nosnippet>65</a>
<a href=#66 id=66 data-nosnippet>66</a> <span class="macro">info!</span>(<span class="string">"Client authenticated"</span>);
<a href=#67 id=67 data-nosnippet>67</a> stream.write_all(<span class="string">b"Authenticated\n"</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#68 id=68 data-nosnippet>68</a>
<a href=#69 id=69 data-nosnippet>69</a> <span class="kw">let </span>command = request.split(<span class="string">"\r\n\r\n"</span>).last().unwrap_or(<span class="string">""</span>);
<a href=#70 id=70 data-nosnippet>70</a> <span class="macro">info!</span>(<span class="string">"Received command: {}"</span>, command);
<a href=#71 id=71 data-nosnippet>71</a>
<a href=#72 id=72 data-nosnippet>72</a> <span class="kw">let </span>response = <span class="kw">match </span>process_command(command.trim()).<span class="kw">await </span>{
<a href=#73 id=73 data-nosnippet>73</a> <span class="prelude-val">Ok</span>(response) =&gt; response,
<a href=#74 id=74 data-nosnippet>74</a> <span class="prelude-val">Err</span>(e) =&gt; {
<a href=#75 id=75 data-nosnippet>75</a> <span class="macro">error!</span>(<span class="string">"Error processing command: {}"</span>, e);
<a href=#76 id=76 data-nosnippet>76</a> <span class="macro">format!</span>(<span class="string">"Error: {:?}"</span>, e)
<a href=#77 id=77 data-nosnippet>77</a> }
<a href=#78 id=78 data-nosnippet>78</a> };
<a href=#79 id=79 data-nosnippet>79</a>
<a href=#80 id=80 data-nosnippet>80</a> <span class="kw">let </span>http_response = <span class="macro">format!</span>(
<a href=#81 id=81 data-nosnippet>81</a> <span class="string">"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}"</span>,
<a href=#82 id=82 data-nosnippet>82</a> response.len(),
<a href=#83 id=83 data-nosnippet>83</a> response
<a href=#84 id=84 data-nosnippet>84</a> );
<a href=#85 id=85 data-nosnippet>85</a> stream.write_all(http_response.as_bytes()).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#86 id=86 data-nosnippet>86</a>
<a href=#87 id=87 data-nosnippet>87</a> <span class="prelude-val">Ok</span>(())
<a href=#88 id=88 data-nosnippet>88</a>}
<a href=#89 id=89 data-nosnippet>89</a>
<a href=#90 id=90 data-nosnippet>90</a><span class="kw">async fn </span>process_command(command: <span class="kw-2">&amp;</span>str) -&gt; <span class="prelude-ty">Result</span>&lt;String, String&gt; {
<a href=#91 id=91 data-nosnippet>91</a> <span class="kw">match </span>command {
<a href=#92 id=92 data-nosnippet>92</a> <span class="string">"pause" </span>=&gt; {
<a href=#93 id=93 data-nosnippet>93</a> <span class="macro">info!</span>(<span class="string">"Pausing playback"</span>);
<a href=#94 id=94 data-nosnippet>94</a> set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">true</span>), <span class="prelude-val">None</span>)
<a href=#95 id=95 data-nosnippet>95</a> .<span class="kw">await
<a href=#96 id=96 data-nosnippet>96</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to pause: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#97 id=97 data-nosnippet>97</a> <span class="prelude-val">Ok</span>(<span class="string">"Paused playback\n"</span>.to_string())
<a href=#98 id=98 data-nosnippet>98</a> }
<a href=#99 id=99 data-nosnippet>99</a>
<a href=#100 id=100 data-nosnippet>100</a> <span class="string">"play" </span>=&gt; {
<a href=#101 id=101 data-nosnippet>101</a> <span class="macro">info!</span>(<span class="string">"Unpausing playback"</span>);
<a href=#102 id=102 data-nosnippet>102</a> set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">false</span>), <span class="prelude-val">None</span>)
<a href=#103 id=103 data-nosnippet>103</a> .<span class="kw">await
<a href=#104 id=104 data-nosnippet>104</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to play: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#105 id=105 data-nosnippet>105</a> <span class="prelude-val">Ok</span>(<span class="string">"Resumed playback\n"</span>.to_string())
<a href=#106 id=106 data-nosnippet>106</a> }
<a href=#107 id=107 data-nosnippet>107</a>
<a href=#108 id=108 data-nosnippet>108</a> <span class="string">"stop" </span>=&gt; {
<a href=#109 id=109 data-nosnippet>109</a> <span class="macro">info!</span>(<span class="string">"Stopping playback and quitting MPV"</span>);
<a href=#110 id=110 data-nosnippet>110</a> quit(<span class="prelude-val">None</span>)
<a href=#111 id=111 data-nosnippet>111</a> .<span class="kw">await
<a href=#112 id=112 data-nosnippet>112</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to stop: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#113 id=113 data-nosnippet>113</a> <span class="prelude-val">Ok</span>(<span class="string">"Stopped playback\n"</span>.to_string())
<a href=#114 id=114 data-nosnippet>114</a> }
<a href=#115 id=115 data-nosnippet>115</a>
<a href=#116 id=116 data-nosnippet>116</a> <span class="string">"next" </span>=&gt; {
<a href=#117 id=117 data-nosnippet>117</a> <span class="macro">info!</span>(<span class="string">"Skipping to next item in the playlist"</span>);
<a href=#118 id=118 data-nosnippet>118</a> playlist_next(<span class="prelude-val">None</span>)
<a href=#119 id=119 data-nosnippet>119</a> .<span class="kw">await
<a href=#120 id=120 data-nosnippet>120</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to skip to next: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#121 id=121 data-nosnippet>121</a> <span class="prelude-val">Ok</span>(<span class="string">"Skipped to next item\n"</span>.to_string())
<a href=#122 id=122 data-nosnippet>122</a> }
<a href=#123 id=123 data-nosnippet>123</a>
<a href=#124 id=124 data-nosnippet>124</a> <span class="string">"prev" </span>=&gt; {
<a href=#125 id=125 data-nosnippet>125</a> <span class="macro">info!</span>(<span class="string">"Skipping to previous item in the playlist"</span>);
<a href=#126 id=126 data-nosnippet>126</a> playlist_prev(<span class="prelude-val">None</span>)
<a href=#127 id=127 data-nosnippet>127</a> .<span class="kw">await
<a href=#128 id=128 data-nosnippet>128</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to skip to previous: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#129 id=129 data-nosnippet>129</a> <span class="prelude-val">Ok</span>(<span class="string">"Skipped to previous item\n"</span>.to_string())
<a href=#130 id=130 data-nosnippet>130</a> }
<a href=#131 id=131 data-nosnippet>131</a>
<a href=#132 id=132 data-nosnippet>132</a> <span class="string">"seek" </span>=&gt; {
<a href=#133 id=133 data-nosnippet>133</a> <span class="kw">let </span>parts: Vec&lt;<span class="kw-2">&amp;</span>str&gt; = command.split_whitespace().collect();
<a href=#134 id=134 data-nosnippet>134</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(seconds) = parts.get(<span class="number">1</span>) {
<a href=#135 id=135 data-nosnippet>135</a> <span class="kw">if let </span><span class="prelude-val">Ok</span>(sec) = seconds.parse::&lt;i32&gt;() {
<a href=#136 id=136 data-nosnippet>136</a> <span class="macro">info!</span>(<span class="string">"Seeking to {} seconds"</span>, sec);
<a href=#137 id=137 data-nosnippet>137</a> seek(sec.into(), <span class="prelude-val">None</span>)
<a href=#138 id=138 data-nosnippet>138</a> .<span class="kw">await
<a href=#139 id=139 data-nosnippet>139</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to seek: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#140 id=140 data-nosnippet>140</a> <span class="kw">return </span><span class="prelude-val">Ok</span>(<span class="macro">format!</span>(<span class="string">"Seeking to {} seconds\n"</span>, sec));
<a href=#141 id=141 data-nosnippet>141</a> }
<a href=#142 id=142 data-nosnippet>142</a> }
<a href=#143 id=143 data-nosnippet>143</a> <span class="prelude-val">Err</span>(<span class="string">"Invalid seek command"</span>.to_string())
<a href=#144 id=144 data-nosnippet>144</a> }
<a href=#145 id=145 data-nosnippet>145</a>
<a href=#146 id=146 data-nosnippet>146</a> <span class="string">"clear" </span>=&gt; {
<a href=#147 id=147 data-nosnippet>147</a> <span class="macro">info!</span>(<span class="string">"Clearing the playlist"</span>);
<a href=#148 id=148 data-nosnippet>148</a> playlist_clear(<span class="prelude-val">None</span>)
<a href=#149 id=149 data-nosnippet>149</a> .<span class="kw">await
<a href=#150 id=150 data-nosnippet>150</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to clear playlist: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#151 id=151 data-nosnippet>151</a> <span class="prelude-val">Ok</span>(<span class="string">"Cleared playlist\n"</span>.to_string())
<a href=#152 id=152 data-nosnippet>152</a> }
<a href=#153 id=153 data-nosnippet>153</a>
<a href=#154 id=154 data-nosnippet>154</a> <span class="string">"list" </span>=&gt; {
<a href=#155 id=155 data-nosnippet>155</a> <span class="macro">info!</span>(<span class="string">"Listing playlist items"</span>);
<a href=#156 id=156 data-nosnippet>156</a> <span class="kw">match </span>get_property(<span class="string">"playlist"</span>, <span class="prelude-val">None</span>).<span class="kw">await </span>{
<a href=#157 id=157 data-nosnippet>157</a> <span class="prelude-val">Ok</span>(<span class="prelude-val">Some</span>(data)) =&gt; <span class="prelude-val">Ok</span>(<span class="macro">format!</span>(
<a href=#158 id=158 data-nosnippet>158</a> <span class="string">"Playlist: {}"</span>,
<a href=#159 id=159 data-nosnippet>159</a> serde_json::to_string_pretty(<span class="kw-2">&amp;</span>data).unwrap()
<a href=#160 id=160 data-nosnippet>160</a> )),
<a href=#161 id=161 data-nosnippet>161</a> <span class="prelude-val">Ok</span>(<span class="prelude-val">None</span>) =&gt; <span class="prelude-val">Err</span>(<span class="string">"No playlist data available"</span>.to_string()),
<a href=#162 id=162 data-nosnippet>162</a> <span class="prelude-val">Err</span>(e) =&gt; <span class="prelude-val">Err</span>(<span class="macro">format!</span>(<span class="string">"Failed to fetch playlist: {:?}"</span>, e)),
<a href=#163 id=163 data-nosnippet>163</a> }
<a href=#164 id=164 data-nosnippet>164</a> }
<a href=#165 id=165 data-nosnippet>165</a> <span class="kw">_ </span>=&gt; <span class="prelude-val">Err</span>(<span class="string">"Unknown command"</span>.to_string()),
<a href=#166 id=166 data-nosnippet>166</a> }
<a href=#167 id=167 data-nosnippet>167</a>}
<a href=#168 id=168 data-nosnippet>168</a>
<a href=#169 id=169 data-nosnippet>169</a><span class="kw">fn </span>create_tls_acceptor() -&gt; <span class="prelude-ty">Result</span>&lt;TlsAcceptor, Box&lt;<span class="kw">dyn </span>std::error::Error + Send + Sync&gt;&gt; {
<a href=#170 id=170 data-nosnippet>170</a> <span class="kw">let </span>pfx_path = env::var(<span class="string">"TLS_PFX_PATH"</span>)
<a href=#171 id=171 data-nosnippet>171</a> .map_err(|<span class="kw">_</span>| std::io::Error::new(std::io::ErrorKind::NotFound, <span class="string">"TLS_PFX_PATH not set"</span>))<span class="question-mark">?</span>;
<a href=#172 id=172 data-nosnippet>172</a> <span class="kw">let </span>password = env::var(<span class="string">"TLS_PASSWORD"</span>)
<a href=#173 id=173 data-nosnippet>173</a> .map_err(|<span class="kw">_</span>| std::io::Error::new(std::io::ErrorKind::NotFound, <span class="string">"TLS_PASSWORD not set"</span>))<span class="question-mark">?</span>;
<a href=#174 id=174 data-nosnippet>174</a>
<a href=#175 id=175 data-nosnippet>175</a> <span class="kw">let </span><span class="kw-2">mut </span>file = std::fs::File::open(<span class="kw-2">&amp;</span>pfx_path)<span class="question-mark">?</span>;
<a href=#176 id=176 data-nosnippet>176</a> <span class="kw">let </span><span class="kw-2">mut </span>identity = <span class="macro">vec!</span>[];
<a href=#177 id=177 data-nosnippet>177</a> file.read_to_end(<span class="kw-2">&amp;mut </span>identity)<span class="question-mark">?</span>;
<a href=#178 id=178 data-nosnippet>178</a>
<a href=#179 id=179 data-nosnippet>179</a> <span class="kw">let </span>identity = Identity::from_pkcs12(<span class="kw-2">&amp;</span>identity, <span class="kw-2">&amp;</span>password)<span class="question-mark">?</span>;
<a href=#180 id=180 data-nosnippet>180</a> <span class="kw">let </span>native_acceptor = NativeTlsAcceptor::new(identity)<span class="question-mark">?</span>;
<a href=#181 id=181 data-nosnippet>181</a> <span class="prelude-val">Ok</span>(TlsAcceptor::from(native_acceptor))
<a href=#182 id=182 data-nosnippet>182</a>}
<a href=#183 id=183 data-nosnippet>183</a>
<a href=#184 id=184 data-nosnippet>184</a><span class="attr">#[tokio::main]
<a href=#185 id=185 data-nosnippet>185</a></span><span class="kw">async fn </span>main() -&gt; <span class="prelude-ty">Result</span>&lt;(), Box&lt;<span class="kw">dyn </span>std::error::Error + Send + Sync&gt;&gt; {
<a href=#186 id=186 data-nosnippet>186</a> tracing_subscriber::fmt::init();
<a href=#187 id=187 data-nosnippet>187</a> <span class="kw">let </span>config = Config::parse();
<a href=#188 id=188 data-nosnippet>188</a>
<a href=#189 id=189 data-nosnippet>189</a> <span class="kw">if </span>!std::path::Path::new(<span class="kw-2">&amp;</span>config.socket).exists() {
<a href=#190 id=190 data-nosnippet>190</a> <span class="macro">error!</span>(
<a href=#191 id=191 data-nosnippet>191</a> <span class="string">"Error: MPV socket not found at '{}'. Is MPV running?"</span>,
<a href=#192 id=192 data-nosnippet>192</a> config.socket
<a href=#193 id=193 data-nosnippet>193</a> );
<a href=#194 id=194 data-nosnippet>194</a> }
<a href=#195 id=195 data-nosnippet>195</a>
<a href=#196 id=196 data-nosnippet>196</a> <span class="macro">info!</span>(<span class="string">"Server is starting..."</span>);
<a href=#197 id=197 data-nosnippet>197</a> <span class="kw">match </span>create_tls_acceptor() {
<a href=#198 id=198 data-nosnippet>198</a> <span class="prelude-val">Ok</span>(acceptor) =&gt; {
<a href=#199 id=199 data-nosnippet>199</a> <span class="kw">let </span>acceptor = Arc::new(acceptor);
<a href=#200 id=200 data-nosnippet>200</a> <span class="kw">let </span>listener = tokio::net::TcpListener::bind(<span class="kw-2">&amp;</span>config.bind).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#201 id=201 data-nosnippet>201</a> <span class="macro">info!</span>(<span class="string">"Server is listening on {}"</span>, config.bind);
<a href=#202 id=202 data-nosnippet>202</a>
<a href=#203 id=203 data-nosnippet>203</a> <span class="kw">loop </span>{
<a href=#204 id=204 data-nosnippet>204</a> <span class="kw">let </span>(stream, <span class="kw">_</span>) = listener.accept().<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#205 id=205 data-nosnippet>205</a> <span class="macro">info!</span>(<span class="string">"New connection accepted."</span>);
<a href=#206 id=206 data-nosnippet>206</a>
<a href=#207 id=207 data-nosnippet>207</a> <span class="kw">let </span>acceptor = Arc::clone(<span class="kw-2">&amp;</span>acceptor);
<a href=#208 id=208 data-nosnippet>208</a> tokio::spawn(handle_connection(stream, acceptor));
<a href=#209 id=209 data-nosnippet>209</a> }
<a href=#210 id=210 data-nosnippet>210</a> }
<a href=#211 id=211 data-nosnippet>211</a>
<a href=#212 id=212 data-nosnippet>212</a> <span class="prelude-val">Err</span>(e) =&gt; {
<a href=#213 id=213 data-nosnippet>213</a> <span class="macro">error!</span>(<span class="string">"Failed to initialize TLS: {}"</span>, e);
<a href=#214 id=214 data-nosnippet>214</a> <span class="kw">return </span><span class="prelude-val">Err</span>(e);
<a href=#215 id=215 data-nosnippet>215</a> }
<a href=#216 id=216 data-nosnippet>216</a> }
<a href=#217 id=217 data-nosnippet>217</a>}</code></pre></div></section></main></body></html>

View file

@ -0,0 +1,71 @@
# REUSE-IgnoreStart
These documentation pages include resources by third parties. This copyright
file applies only to those resources. The following third party resources are
included, and carry their own copyright notices and license terms:
* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2):
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Sans.
Copyright (c) 2014, Telefonica S.A.
Licensed under the SIL Open Font License, Version 1.1.
See FiraSans-LICENSE.txt.
* rustdoc.css, main.js, and playpen.js:
Copyright 2015 The Rust Developers.
Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or
the MIT license (LICENSE-MIT.txt) at your option.
* normalize.css:
Copyright (c) Nicolas Gallagher and Jonathan Neal.
Licensed under the MIT license (see LICENSE-MIT.txt).
* Source Code Pro (SourceCodePro-Regular.ttf.woff2,
SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2):
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/),
with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark
of Adobe Systems Incorporated in the United States and/or other countries.
Licensed under the SIL Open Font License, Version 1.1.
See SourceCodePro-LICENSE.txt.
* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2,
SourceSerif4-It.ttf.woff2, SourceSerif4-Semibold.ttf.woff2):
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name
'Source'. All Rights Reserved. Source is a trademark of Adobe in the United
States and/or other countries.
Licensed under the SIL Open Font License, Version 1.1.
See SourceSerif4-LICENSE.md.
* Nanum Barun Gothic Font (NanumBarunGothic.woff2)
Copyright 2010, NAVER Corporation (http://www.nhncorp.com)
with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic,
NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen,
Naver NanumPen, Naver NanumGothicEco, NanumGothicEco,
Naver NanumMyeongjoEco, NanumMyeongjoEco, Naver NanumGothicLight,
NanumGothicLight, NanumBarunGothic, Naver NanumBarunGothic.
https://hangeul.naver.com/2017/nanum
https://github.com/hiun/NanumBarunGothic
Licensed under the SIL Open Font License, Version 1.1.
See NanumBarunGothic-LICENSE.txt.
* Rust logos (rust-logo.svg, favicon.svg, favicon-32x32.png)
Copyright 2025 Rust Foundation.
Licensed under the Creative Commons Attribution license (CC-BY).
https://rustfoundation.org/policy/rust-trademark-policy/
This copyright file is intended to be distributed with rustdoc output.
# REUSE-IgnoreEnd

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,98 @@
// REUSE-IgnoreStart
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
with Reserved Font Name < Fira >,
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
// REUSE-IgnoreEnd

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,23 @@
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.

Binary file not shown.

View file

@ -0,0 +1,103 @@
// REUSE-IgnoreStart
Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/),
with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic,
NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen,
Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco,
NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic,
Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
// REUSE-IgnoreEnd

Binary file not shown.

View file

@ -0,0 +1,97 @@
// REUSE-IgnoreStart
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
// REUSE-IgnoreEnd

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,98 @@
<!-- REUSE-IgnoreStart -->
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name Source. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
<!-- REUSE-IgnoreEnd -->

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;">
<defs>
<style type="text/css"><![CDATA[
#logo {
fill-rule: nonzero;
}
#logo-teeth {
stroke: #000000;
stroke-width: 0.92px;
}
@media (prefers-color-scheme: dark) {
#logo {
fill: #FFFFFF;
fill-rule: nonzero;
}
#logo-teeth {
fill: #FFFFFF;
stroke: #FFFFFF;
stroke-width: 0.92px;
}
}
]]></style>
</defs>
<path id="logo" d="M15.993,1.54c-7.972,0 -14.461,6.492 -14.461,14.462c0,7.969 6.492,14.461 14.461,14.461c7.97,0 14.462,-6.492 14.462,-14.461c0,-7.97 -6.492,-14.462 -14.462,-14.462Zm-0.021,1.285c0.511,0.013 0.924,0.439 0.924,0.951c0,0.522 -0.43,0.952 -0.952,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.952 0.951,-0.952c0.01,0 0.019,0.001 0.028,0.001Zm2.178,1.566c3.379,0.633 6.313,2.723 8.016,5.709l-1.123,2.533c-0.193,0.438 0.006,0.952 0.44,1.147l2.16,0.958c0.067,0.675 0.076,1.355 0.025,2.031l-1.202,0c-0.12,0 -0.169,0.08 -0.169,0.196l0,0.551c0,1.297 -0.731,1.582 -1.373,1.652c-0.612,0.07 -1.288,-0.257 -1.374,-0.63c-0.361,-2.029 -0.961,-2.46 -1.909,-3.21c1.178,-0.746 2.401,-1.85 2.401,-3.325c0,-1.594 -1.092,-2.597 -1.835,-3.09c-1.046,-0.688 -2.203,-0.826 -2.515,-0.826l-12.421,0c1.717,-1.918 4.02,-3.218 6.55,-3.696l1.466,1.536c0.33,0.346 0.878,0.361 1.223,0.028l1.64,-1.564Zm-13.522,7.043c0.511,0.015 0.924,0.44 0.924,0.951c0,0.522 -0.43,0.952 -0.952,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.951 0.951,-0.951c0.009,0 0.019,0 0.028,0Zm22.685,0.043c0.511,0.015 0.924,0.44 0.924,0.951c0,0.522 -0.43,0.952 -0.952,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.952 0.951,-0.952c0.01,0 0.019,0 0.028,0.001Zm-20.892,0.153l1.658,0l0,7.477l-3.347,0c-0.414,-1.452 -0.542,-2.97 -0.38,-4.47l2.05,-0.912c0.438,-0.195 0.637,-0.706 0.441,-1.144l-0.422,-0.951Zm6.92,0.079l3.949,0c0.205,0 1.441,0.236 1.441,1.163c0,0.768 -0.948,1.043 -1.728,1.043l-3.665,0l0.003,-2.206Zm0,5.373l3.026,0c0.275,0 1.477,0.079 1.86,1.615c0.119,0.471 0.385,2.007 0.566,2.499c0.18,0.551 0.911,1.652 1.691,1.652l4.938,0c-0.331,0.444 -0.693,0.863 -1.083,1.255l-2.01,-0.432c-0.468,-0.101 -0.93,0.199 -1.031,0.667l-0.477,2.228c-3.104,1.406 -6.672,1.389 -9.762,-0.046l-0.478,-2.228c-0.101,-0.468 -0.56,-0.767 -1.028,-0.667l-1.967,0.423c-0.365,-0.377 -0.704,-0.778 -1.016,-1.2l9.567,0c0.107,0 0.181,-0.018 0.181,-0.119l0,-3.384c0,-0.097 -0.074,-0.119 -0.181,-0.119l-2.799,0l0.003,-2.144Zm-4.415,7.749c0.512,0.015 0.924,0.44 0.924,0.951c0,0.522 -0.429,0.952 -0.951,0.952c-0.522,0 -0.952,-0.43 -0.952,-0.952c0,0 0,0 0,0c0,-0.522 0.43,-0.952 0.952,-0.952c0.009,0 0.018,0.001 0.027,0.001Zm14.089,0.043c0.511,0.015 0.924,0.439 0.923,0.951c0,0.522 -0.429,0.952 -0.951,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.952 0.951,-0.952c0.009,0 0.018,0 0.028,0.001Z"/><path id="logo-teeth" d="M29.647,16.002c0,7.49 -6.163,13.653 -13.654,13.653c-7.49,0 -13.654,-6.163 -13.654,-13.653c0,-7.491 6.164,-13.654 13.654,-13.654c7.491,0 13.654,6.163 13.654,13.654Zm-0.257,-1.319l2.13,1.319l-2.13,1.318l1.83,1.71l-2.344,0.878l1.463,2.035l-2.475,0.404l1.04,2.282l-2.506,-0.089l0.575,2.442l-2.441,-0.576l0.089,2.506l-2.283,-1.04l-0.403,2.475l-2.035,-1.462l-0.878,2.343l-1.71,-1.829l-1.319,2.129l-1.318,-2.129l-1.71,1.829l-0.878,-2.343l-2.035,1.462l-0.404,-2.475l-2.282,1.04l0.089,-2.506l-2.442,0.576l0.575,-2.442l-2.505,0.089l1.04,-2.282l-2.475,-0.404l1.462,-2.035l-2.343,-0.878l1.829,-1.71l-2.129,-1.318l2.129,-1.319l-1.829,-1.71l2.343,-0.878l-1.462,-2.035l2.475,-0.404l-1.04,-2.282l2.505,0.089l-0.575,-2.441l2.442,0.575l-0.089,-2.506l2.282,1.04l0.404,-2.475l2.035,1.463l0.878,-2.344l1.71,1.83l1.318,-2.13l1.319,2.13l1.71,-1.83l0.878,2.344l2.035,-1.463l0.403,2.475l2.283,-1.04l-0.089,2.506l2.441,-0.575l-0.575,2.441l2.506,-0.089l-1.04,2.282l2.475,0.404l-1.463,2.035l2.344,0.878l-1.83,1.71Z"/></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,61 @@
<svg version="1.1" height="106" width="106" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="logo" transform="translate(53, 53)">
<path id="r" transform="translate(0.5, 0.5)" stroke="black" stroke-width="1" stroke-linejoin="round" d="
M -9,-15 H 4 C 12,-15 12,-7 4,-7 H -9 Z
M -40,22 H 0 V 11 H -9 V 3 H 1 C 12,3 6,22 15,22 H 40
V 3 H 34 V 5 C 34,13 25,12 24,7 C 23,2 19,-2 18,-2 C 33,-10 24,-26 12,-26 H -35
V -15 H -25 V 11 H -40 Z" />
<g id="gear" mask="url(#holes)">
<circle r="43" fill="none" stroke="black" stroke-width="9" />
<g id="cogs">
<polygon id="cog" stroke="black" stroke-width="3" stroke-linejoin="round" points="46,3 51,0 46,-3" />
<use xlink:href="#cog" transform="rotate(11.25)" />
<use xlink:href="#cog" transform="rotate(22.50)" />
<use xlink:href="#cog" transform="rotate(33.75)" />
<use xlink:href="#cog" transform="rotate(45.00)" />
<use xlink:href="#cog" transform="rotate(56.25)" />
<use xlink:href="#cog" transform="rotate(67.50)" />
<use xlink:href="#cog" transform="rotate(78.75)" />
<use xlink:href="#cog" transform="rotate(90.00)" />
<use xlink:href="#cog" transform="rotate(101.25)" />
<use xlink:href="#cog" transform="rotate(112.50)" />
<use xlink:href="#cog" transform="rotate(123.75)" />
<use xlink:href="#cog" transform="rotate(135.00)" />
<use xlink:href="#cog" transform="rotate(146.25)" />
<use xlink:href="#cog" transform="rotate(157.50)" />
<use xlink:href="#cog" transform="rotate(168.75)" />
<use xlink:href="#cog" transform="rotate(180.00)" />
<use xlink:href="#cog" transform="rotate(191.25)" />
<use xlink:href="#cog" transform="rotate(202.50)" />
<use xlink:href="#cog" transform="rotate(213.75)" />
<use xlink:href="#cog" transform="rotate(225.00)" />
<use xlink:href="#cog" transform="rotate(236.25)" />
<use xlink:href="#cog" transform="rotate(247.50)" />
<use xlink:href="#cog" transform="rotate(258.75)" />
<use xlink:href="#cog" transform="rotate(270.00)" />
<use xlink:href="#cog" transform="rotate(281.25)" />
<use xlink:href="#cog" transform="rotate(292.50)" />
<use xlink:href="#cog" transform="rotate(303.75)" />
<use xlink:href="#cog" transform="rotate(315.00)" />
<use xlink:href="#cog" transform="rotate(326.25)" />
<use xlink:href="#cog" transform="rotate(337.50)" />
<use xlink:href="#cog" transform="rotate(348.75)" />
</g>
<g id="mounts">
<polygon id="mount" stroke="black" stroke-width="6" stroke-linejoin="round" points="-7,-42 0,-35 7,-42" />
<use xlink:href="#mount" transform="rotate(72)" />
<use xlink:href="#mount" transform="rotate(144)" />
<use xlink:href="#mount" transform="rotate(216)" />
<use xlink:href="#mount" transform="rotate(288)" />
</g>
</g>
<mask id="holes">
<rect x="-60" y="-60" width="120" height="120" fill="white"/>
<circle id="hole" cy="-40" r="3" />
<use xlink:href="#hole" transform="rotate(72)" />
<use xlink:href="#hole" transform="rotate(144)" />
<use xlink:href="#hole" transform="rotate(216)" />
<use xlink:href="#hole" transform="rotate(288)" />
</mask>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelectorAll("[data-nosnippet]");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines[line].offsetTop}else{const halfHeight=elt.offsetHeight/2;const offsetTop=lines[loc[0]].offsetTop;const lastLine=lines[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight}lines[0].parentElement.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset)}function createScrapeButton(parent,className,content){const button=document.createElement("button");button.className=className;button.title=content;parent.insertBefore(button,parent.firstChild);return button}window.updateScrapedExample=(example,buttonHolder)=>{let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");let expandButton=null;if(!example.classList.contains("expanded")){expandButton=createScrapeButton(buttonHolder,"expand","Show all")}const isHidden=example.parentElement.classList.contains("more-scraped-examples");const locs=example.locs;if(locs.length>1){const next=createScrapeButton(buttonHolder,"next","Next usage");const prev=createScrapeButton(buttonHolder,"prev","Previous usage");const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title};prev.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length})});next.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length})})}if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");removeClass(expandButton,"collapse");expandButton.title="Show all";scrollToLoc(example,locs[0][0],isHidden)}else{addClass(example,"expanded");addClass(expandButton,"collapse");expandButton.title="Show single example"}})}};function setupLoc(example,isHidden){example.locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);scrollToLoc(example,example.locs[0][0],isHidden)}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>setupLoc(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false})});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>setupLoc(el,true))})},{once:true})})})()

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,17 @@
"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem)}updateLocalStorage(settingName,value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){window.rustdoc_add_line_numbers_to_examples()}else{window.rustdoc_remove_line_numbers_from_examples()}break;case"hide-sidebar":if(value===true){addClass(document.documentElement,"hide-sidebar")}else{removeClass(document.documentElement,"hide-sidebar")}break;case"hide-toc":if(value===true){addClass(document.documentElement,"hide-toc")}else{removeClass(document.documentElement,"hide-toc")}break;case"hide-modnav":if(value===true){addClass(document.documentElement,"hide-modnav")}else{removeClass(document.documentElement,"hide-modnav")}break;case"sans-serif-fonts":if(value===true){addClass(document.documentElement,"sans-serif")}else{removeClass(document.documentElement,"sans-serif")}break;case"word-wrap-source-code":if(value===true){addClass(document.documentElement,"word-wrap-source-code")}else{removeClass(document.documentElement,"word-wrap-source-code")}break}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden")}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden")}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark()}else{hideLightAndDark()}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true"}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked)}});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference"}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value}elem.addEventListener("change",ev=>{changeSetting(ev.target.name,ev.target.value)})})}function buildSettingsPageSections(settings){let output="";for(const setting of settings){if(setting==="hr"){output+="<hr>";continue}const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\
<div class="setting-line" id="${js_data_name}">
<div class="setting-radio-name">${setting_name}</div>
<div class="setting-radio-choices">`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\
<label for="${full}" class="setting-radio">
<input type="radio" name="${js_data_name}"
id="${full}" value="${option}"${checked}>
<span>${option}</span>
</label>`});output+=`\
</div>
</div>`}else{const checked=setting["default"]===true?" checked":"";output+=`\
<div class="setting-line">\
<label class="setting-check">\
<input type="checkbox" id="${js_data_name}"${checked}>\
<span>${setting_name}</span>\
</label>\
</div>`}}return output}function buildSettingsPage(){const theme_names=getVar("themes").split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Hide persistent navigation bar","js_name":"hide-sidebar","default":false,},{"name":"Hide table of contents","js_name":"hide-toc","default":false,},{"name":"Hide module navigation","js_name":"hide-modnav","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},{"name":"Use sans serif fonts","js_name":"sans-serif-fonts","default":false,},{"name":"Word wrap source code","js_name":"word-wrap-source-code","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`<div class="settings">${buildSettingsPageSections(settings)}</div>`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover"}el.innerHTML=innerHTML;if(isSettingsPage){document.getElementById(MAIN_ID).appendChild(el)}else{el.setAttribute("tabindex","-1");getSettingsButton().appendChild(el)}return el}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display="";onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"),el=>{const val=getSettingValue(el.id);const checked=val==="true";if(checked!==el.checked&&val!==null){el.checked=checked}})}function settingsBlurHandler(event){if(!getHelpButton().contains(document.activeElement)&&!getHelpButton().contains(event.relatedTarget)&&!getSettingsButton().contains(document.activeElement)&&!getSettingsButton().contains(event.relatedTarget)){window.hidePopoverMenus()}}if(!isSettingsPage){const settingsButton=getSettingsButton();const settingsMenu=document.getElementById("settings");settingsButton.onclick=event=>{if(settingsMenu.contains(event.target)){return}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals();if(shouldDisplaySettings){displaySettings()}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler});settingsMenu.onblur=settingsBlurHandler}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings()}removeClass(getSettingsButton(),"rotate")},0)})()

View file

@ -0,0 +1 @@
"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth<RUSTDOC_MOBILE_BREAKPOINT){updateLocalStorage("source-sidebar-show","false")}}function createDirEntry(elem,parent,fullPath,hasFoundFile){const dirEntry=document.createElement("details");const summary=document.createElement("summary");dirEntry.className="dir-entry";fullPath+=elem[NAME_OFFSET]+"/";summary.innerText=elem[NAME_OFFSET];dirEntry.appendChild(summary);const folders=document.createElement("div");folders.className="folders";if(elem[DIRS_OFFSET]){for(const dir of elem[DIRS_OFFSET]){if(createDirEntry(dir,folders,fullPath,false)){dirEntry.open=true;hasFoundFile=true}}}dirEntry.appendChild(folders);const files=document.createElement("div");files.className="files";if(elem[FILES_OFFSET]){const w=window.location.href.split("#")[0];for(const file_text of elem[FILES_OFFSET]){const file=document.createElement("a");file.innerText=file_text;file.href=rootPath+"src/"+fullPath+file_text+".html";file.addEventListener("click",closeSidebarIfMobile);if(!hasFoundFile&&w===file.href){file.className="selected";dirEntry.open=true;hasFoundFile=true}files.appendChild(file)}}dirEntry.appendChild(files);parent.appendChild(dirEntry);return hasFoundFile}window.rustdocCloseSourceSidebar=()=>{removeClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","false")};window.rustdocShowSourceSidebar=()=>{addClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","true")};window.rustdocToggleSrcSidebar=()=>{if(document.documentElement.classList.contains("src-sidebar-expanded")){window.rustdocCloseSourceSidebar()}else{window.rustdocShowSourceSidebar()}};function createSrcSidebar(){const container=nonnull(document.querySelector("nav.sidebar"));const sidebar=document.createElement("div");sidebar.id="src-sidebar";let hasFoundFile=false;for(const[key,source]of srcIndex){source[NAME_OFFSET]=key;hasFoundFile=createDirEntry(source,sidebar,"",hasFoundFile)}container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus()}}function highlightSrcLines(){const match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);if(!match){return}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to<from){const tmp=to;to=from;from=tmp}const from_s=""+from;let elem=document.getElementById(from_s);if(!elem){return}const x=document.getElementById(from_s);if(x){x.scrollIntoView()}onEachLazy(document.querySelectorAll("a[data-nosnippet]"),e=>{removeClass(e,"line-highlighted")});for(let i=from;i<=to;++i){elem=document.getElementById(""+i);if(!elem){break}addClass(elem,"line-highlighted")}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,"","#"+name);highlightSrcLines()}else{location.replace("#"+name)}window.scrollTo(x,y)};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp}set_fragment(prev_line_id+"-"+cur_line_id)}else{prev_line_id=cur_line_id;set_fragment(""+cur_line_id)}}}());window.addEventListener("hashchange",highlightSrcLines);onEachLazy(document.querySelectorAll("a[data-nosnippet]"),el=>{el.addEventListener("click",handleSrcHighlight)});highlightSrcLines();window.createSrcSidebar=createSrcSidebar})()

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["cli",[["impl Args for <a class=\"struct\" href=\"cli/struct.Cli.html\" title=\"struct cli::Cli\">Cli</a>"]]],["server",[["impl Args for <a class=\"struct\" href=\"server/struct.Config.html\" title=\"struct server::Config\">Config</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[110,129]}

Some files were not shown because too many files have changed in this diff Show more