Compare commits

...

5 Commits

Author SHA1 Message Date
~erin bfbc791a7b
Read options from configuration file 2023-03-22 00:16:00 -04:00
~erin e7fcad9049
Keybindings for controlling MPD 2023-03-21 23:27:44 -04:00
~erin 701345de29
Subcommands for controlling MPD 2023-03-21 23:24:40 -04:00
~erin 70b77cfb72
Add basic commandline options 2023-03-21 23:13:31 -04:00
~erin 1bfaecb74f
Add Makefile 2023-03-21 22:44:15 -04:00
6 changed files with 549 additions and 19 deletions

403
Cargo.lock generated
View File

@ -14,6 +14,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
[[package]]
name = "bufstream"
version = "0.1.4"
@ -26,19 +32,62 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098"
dependencies = [
"bitflags 2.0.2",
"clap_derive",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "4.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "clap_lex"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "crossterm"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"crossterm_winapi",
"libc",
"mio",
@ -57,12 +106,121 @@ dependencies = [
"winapi",
]
[[package]]
name = "dirs"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b"
dependencies = [
"libc",
"redox_users",
"windows-sys",
]
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "getrandom"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "indexmap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "io-lifetimes"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]]
name = "libc"
version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "lock_api"
version = "0.4.9"
@ -82,6 +240,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mio"
version = "0.8.6"
@ -105,6 +269,18 @@ dependencies = [
"time",
]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "os_str_bytes"
version = "6.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
[[package]]
name = "paris"
version = "1.5.13"
@ -134,13 +310,66 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
"bitflags 1.3.2",
]
[[package]]
name = "redox_users"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom",
"redox_syscall",
"thiserror",
]
[[package]]
@ -149,12 +378,55 @@ version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
[[package]]
name = "rustix"
version = "0.36.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e"
dependencies = [
"bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.4",
]
[[package]]
name = "serde_spanned"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
dependencies = [
"serde",
]
[[package]]
name = "signal-hook"
version = "0.3.15"
@ -191,6 +463,63 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.4",
]
[[package]]
name = "time"
version = "0.1.45"
@ -202,19 +531,59 @@ dependencies = [
"winapi",
]
[[package]]
name = "toml"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "tui"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"cassowary",
"crossterm",
"unicode-segmentation",
"unicode-width",
]
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
@ -227,6 +596,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
@ -255,6 +630,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
@ -327,12 +711,25 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "winnow"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966"
dependencies = [
"memchr",
]
[[package]]
name = "xenmotif"
version = "0.1.0"
dependencies = [
"clap",
"crossterm",
"dirs",
"mpd",
"paris",
"serde",
"toml",
"tui",
]

View File

@ -24,10 +24,13 @@ tui = "0.19"
crossterm = "0.25"
mpd = "*"
paris = { version = "1.5", features = ["no_logger", "macros"] }
clap = { version = "4.1.11", features = ["derive"] }
toml = "0.7.3"
serde = { version = "1.0.158", features = ["derive"] }
dirs = "5.0"
[profile.release]
strip = true
opt-level = "z"
lto = true
codegen-units = 1
#upx --best --lzma target/release/min-sized-rust

5
Makefile Normal file
View File

@ -0,0 +1,5 @@
debug:
@cargo run
release:
@cargo build --release
@upx --best --lzma target/release/xenmotif

View File

@ -6,19 +6,32 @@ A simple `TUI` and commandline client for `mpd`.
- Modern features such as album art, scrobbling, notifications, etc.
- Simple configuration via a `TOML` file, and commandline options
## Configuration
If a configuration file is not found, `xenmotif` will create a new `config.toml` file in the OS config directory.
A custom configuration file can be passed as an argument, and arguments will override options in the config file.
## Keybindings
`p` - Toggle play/pause
`s` - Stop
`i` - Insert mode (search)
`<esc>` - Return to normal mode
`q` - Quit
## API (WIP)
### Commandline Options
`--help` - Print help options
`-h/--help` - Print help options
`-p/--port` - Specify MPD port
`--port` - Specify MPD port
`-h/--host` - Specify MPD host
`--host` - Specify MPD host
`-c/--config` - Use a specific configuration file
`-v/--version` - Print version information
`-j/--json` - Output in JSON format
`-V/--version` - Print version information
### Subcommands
`stop` - Stop playing
@ -26,7 +39,3 @@ A simple `TUI` and commandline client for `mpd`.
`play` - Start/resume playing
`pause` - Pause playing
`queue` - Print off queue
`info` - Print current song information

View File

@ -6,6 +6,9 @@ use crossterm::{
use std::{
error::Error,
io,
env,
fs,
path::PathBuf,
time::{Duration, Instant},
process::exit,
};
@ -18,12 +21,123 @@ use tui::{
Frame, Terminal,
};
use paris::error;
use paris::{info, error};
use clap::{Parser, Subcommand, ValueEnum};
use toml::Table;
use serde::{Serialize, Deserialize};
mod structs;
use structs::{App, StatefulList, InputMode};
#[derive(Deserialize, Serialize)]
struct Config {
host: Option<String>,
port: Option<u16>,
}
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// The port of the MPD server to connect to
#[arg(long)]
port: Option<u16>,
/// The IP of the MPD server to connect to
#[arg(long)]
host: Option<String>,
/// Specify a configuration file
#[arg(short, long)]
config: Option<String>,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Debug, Subcommand)]
enum Commands {
/// Stop playing
Stop,
/// Start/resume playing
Play,
/// Pause playing
Pause,
}
fn main() -> Result<(), Box<dyn Error>> {
// Parse arguments
let args = Args::parse();
let mut default_config = dirs::config_dir().unwrap();
default_config.push("xenmotif");
default_config.push("config.toml");
let config_file = match args.config {
Some(file) => PathBuf::from(file),
None => default_config.clone(),
};
let config_contents = match fs::read_to_string(&config_file) {
Ok(f) => f,
Err(e) => {
error!("Unable to read config file! {}", e);
let config = Config {
host: Some("127.0.0.1".to_string()),
port: Some(6600),
};
let toml = toml::to_string(&config).unwrap();
default_config.pop();
fs::create_dir_all(&default_config).unwrap();
default_config.push("config.toml");
fs::write(&default_config, toml).unwrap();
info!("Created new config file at {}", &default_config.display());
exit(0);
},
};
let config: Config = match toml::from_str(&config_contents) {
Ok(s) => s,
Err(e) => {
error!("Couldn't parse config file! {}", e);
exit(1);
},
};
let port = match args.port {
Some(p) => p,
None => match config.port {
Some(h) => h,
None => 6600,
},
};
let host = match args.host {
Some(h) => h,
None => match config.host {
Some(h) => h,
None => "127.0.0.1".to_string(),
},
};
let mut app = App::new(port, host);
match args.command {
Some(Commands::Stop) => {
app.mpd_client.stop().unwrap();
exit(0);
},
Some(Commands::Play) => {
app.mpd_client.play().unwrap();
exit(0);
},
Some(Commands::Pause) => {
app.mpd_client.pause(true).unwrap();
exit(0);
}
None => {},
}
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
@ -33,7 +147,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// create app and run it
let tick_rate = Duration::from_millis(50);
let app = App::new();
let res = run_app(&mut terminal, app, tick_rate);
// restore terminal
@ -68,6 +182,8 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App, tick_rate: Dura
KeyCode::Char('h') => app.items.unselect(),
KeyCode::Char('j') => app.items.next(),
KeyCode::Char('k') => app.items.previous(),
KeyCode::Char('p') => app.mpd_client.toggle_pause().unwrap(),
KeyCode::Char('s') => app.mpd_client.stop().unwrap(),
KeyCode::Char('i') => app.input_mode = InputMode::Editing,
_ => {}
},

View File

@ -5,7 +5,6 @@ use tui::{
use paris::error;
use std::process::exit;
pub struct StatefulList<T> {
pub state: ListState,
pub items: Vec<T>,
@ -67,8 +66,8 @@ pub struct App<'a> {
}
impl<'a> App<'a> {
pub fn new() -> App<'a> {
let client = match Client::connect("127.0.0.1:6600") {
pub fn new(port: u16, host: String) -> App<'a> {
let client = match Client::connect(format!("{}:{}", host, port)) {
Ok(conn) => {
conn
},
@ -92,3 +91,4 @@ pub enum InputMode {
Normal,
Editing,
}