diff options
| author | Rubens Brandao <git@rubens.io> | 2021-04-09 20:09:37 +0200 |
|---|---|---|
| committer | Rubens Brandao <git@rubens.io> | 2021-04-09 20:09:37 +0200 |
| commit | 47c47bf462d508176060f575960d96f01908bb70 (patch) | |
| tree | 9e3f7b328ae74e7c80b0843ffc66ce29a17108de | |
| parent | 7c5f60f210bfd05ea22d3a65f04e245989fdaade (diff) | |
| parent | a13d4b7d10aff81823b8cabf1d72daa881d36065 (diff) | |
| download | mum-47c47bf462d508176060f575960d96f01908bb70.tar.gz | |
Merge branch 'main' into multiple-audio
| -rw-r--r-- | CHANGELOG | 31 | ||||
| -rw-r--r-- | Cargo.lock | 145 | ||||
| -rw-r--r-- | documentation/mumctl.1 | 20 | ||||
| -rw-r--r-- | documentation/mumctl.txt | 14 | ||||
| -rw-r--r-- | documentation/mumd.1 | 6 | ||||
| -rw-r--r-- | documentation/mumd.txt | 2 | ||||
| -rw-r--r-- | documentation/mumdrc.5 | 6 | ||||
| -rw-r--r-- | documentation/mumdrc.txt | 2 | ||||
| -rw-r--r-- | mumctl/Cargo.toml | 7 | ||||
| -rw-r--r-- | mumctl/build.rs | 12 | ||||
| -rw-r--r-- | mumctl/src/main.rs | 949 | ||||
| -rw-r--r-- | mumd/Cargo.toml | 1 | ||||
| -rw-r--r-- | mumd/src/network/udp.rs | 8 | ||||
| -rw-r--r-- | mumlib/Cargo.toml | 5 | ||||
| -rw-r--r-- | mumlib/src/error.rs | 7 |
15 files changed, 616 insertions, 599 deletions
@@ -44,31 +44,30 @@ Other * Updated to tokio 1.0. * Cleaned up dependencies -0.3 - 2020-12-25 +0.3.1 - 2021-04-08 +------------------ + +Fixed +~~~~~ + + * Compilation no longer fails with "module export is private". + +0.3.0 - 2020-12-25 ------------------ Added ~~~~~ * Published to crates.io. You can now install with +cargo install {mumctl,mumd}+! - * Support for macOS. - * Notifications via libnotify (behind a feature-gate). - * List configured servers, if they're online and how many users are connected with +mumctl status+. - * Configure your own outgoing volume and others' ingoing volume with +mumctl config+. - * Mute/deafen yourself with +mumctl {mute,deafen}+. - * Locally mute others with +mumctl user <user> mute+. - * Sound effects. - * Short about-texts when using +--help+. - * Man pages. Changed @@ -89,24 +88,22 @@ Other * Moved to dasp, a pure Rust digital audio signal processing library. -0.2 - 2020-10-26 +0.2.0 - 2020-10-26 ------------------ Added ~~~~~ * Connect to and disconnect from servers with +mumctl server {connect,disconnect}+. - * Save and reconfigure servers with +mumctl server {add,remove,rename,config}+. - * List channels and connect to them with +mumctl channel {list,join}+. - * Store configuration in a file. - * Generate CLI completions for zsh, bash and fish with +mumctl completions --{zsh,bash,fish}+. Links ----- -[0.3]: https://github.com/sornas/mum/tree/v0.3 -[0.2]: https://github.com/sornas/mum/tree/v0.2 +[0.3.2]: https://github.com/mum-rs/mum/tree/v0.3.2 +[0.3.1]: https://github.com/mum-rs/mum/tree/v0.3.1 +[0.3.0]: https://github.com/mum-rs/mum/tree/v0.3.0 +[0.2.0]: https://github.com/mum-rs/mum/tree/v0.2.0 @@ -184,7 +184,6 @@ dependencies = [ "textwrap", "unicode-width", "vec_map", - "yaml-rust", ] [[package]] @@ -257,9 +256,9 @@ dependencies = [ [[package]] name = "cpal" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "840981d3f30230d9120328d64be72319dbbedabb61bcd4c370a54cdd051238ac" +checksum = "8351ddf2aaa3c583fa388029f8b3d26f3c7035a20911fdd5f2e2ed7ab57dad25" dependencies = [ "alsa", "core-foundation-sys 0.6.2", @@ -410,9 +409,9 @@ dependencies = [ [[package]] name = "dasp_window" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66bcb90ea007ba45fc48d426e28af3e8a653634f9a7174d768dcfe90fa6211f4" +checksum = "99ded7b88821d2ce4e8b842c9f1c86ac911891ab89443cc1de750cae764c5076" dependencies = [ "dasp_sample", ] @@ -708,9 +707,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -729,9 +728,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" +checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714" [[package]] name = "libloading" @@ -806,9 +805,9 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "mio" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2182a122f3b7f3f5329cb1972cee089ba2459a0a80a56935e6e674f096f8d839" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" dependencies = [ "libc", "log", @@ -819,11 +818,10 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ - "socket2", "winapi", ] @@ -846,10 +844,10 @@ name = "mumctl" version = "0.3.0" dependencies = [ "bincode", - "clap", "colored", "log", "mumlib", + "structopt", ] [[package]] @@ -1188,6 +1186,30 @@ dependencies = [ ] [[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", + "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-macro-hack" version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1201,9 +1223,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -1377,9 +1399,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d493c5f39e02dfb062cd8f33301f90f9b13b650e8c1b1d0fd75c19dd64bff69d" +checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84" dependencies = [ "bitflags", "core-foundation", @@ -1390,9 +1412,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee48cdde5ed250b0d3252818f646e174ab414036edb884dde62d80a3ac6082d" +checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339" dependencies = [ "core-foundation-sys 0.8.2", "libc", @@ -1400,18 +1422,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2", "quote", @@ -1437,17 +1459,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] -name = "socket2" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" -dependencies = [ - "cfg-if", - "libc", - "winapi", -] - -[[package]] name = "stdweb" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1466,6 +1477,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "strum" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1485,9 +1520,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.64" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" dependencies = [ "proc-macro2", "quote", @@ -1644,9 +1679,9 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi", @@ -1667,9 +1702,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1677,9 +1712,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", @@ -1692,9 +1727,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1702,9 +1737,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", @@ -1715,15 +1750,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "web-sys" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310" +checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" dependencies = [ "js-sys", "wasm-bindgen", @@ -1759,9 +1794,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "yaml-rust" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" diff --git a/documentation/mumctl.1 b/documentation/mumctl.1 index 2752ad6..16360bb 100644 --- a/documentation/mumctl.1 +++ b/documentation/mumctl.1 @@ -2,12 +2,12 @@ .\" Title: mumd .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.12 -.\" Date: 2021-03-29 +.\" Date: 2021-04-03 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "MUMCTL" "1" "2021-03-29" "\ \&" "\ \&" +.TH "MUMCTL" "1" "2021-04-03" "\ \&" "\ \&" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 @@ -66,9 +66,9 @@ mumctl completions [\-\-bash|\-\-fish|\-\-zsh] Generate a completion file for the specified shell. .RE .sp -mumctl config <name> <value> +mumctl config <key> <value> .RS 4 -Set a configuration value in the mumd(1) config\-file. +Set a configuration value in the mumd(1) config\-file. See mumdrc(5). .RE .sp mumctl config\-reload @@ -76,10 +76,10 @@ mumctl config\-reload Force a reload of the configuration file (e.g. after editing it externally. .RE .sp -mumctl connect [\-p|\-\-port <port>] <host> [username] +mumctl connect <host> [\-p|\-\-port <port>] [username] .RS 4 Connect to a server on the specified port. The host may be either the name -of a saved server or an IP or a URL (in which case username needs to be passed +of a saved server or an IP/URL (in which case username needs to be passed as well). If omitted, the port defaults to 64738. .RE @@ -105,12 +105,12 @@ Mute yourself or someone else. If user is omitted, you mute yourself. Otherwise, the user with the username [user] is muted. .RE .sp -mumctl server add [\-\-password <password>] [\-\-port <port>] [\-\-username <username>] [<name>] <host> +mumctl server add <name> <host> [\-\-port <port>] [<username> | <username> <password>] .RS 4 Add a saved server configuration. .RE .sp -mumctl server config <server> <name> <value> +mumctl server config <server> <key> <value> .RS 4 Configure a variable in a saved server configuration. .RE @@ -163,9 +163,9 @@ Gustav Sörnäs and Eskil Queseth. .SH "REPORTING BUGS" .sp Please report bugs to the Github repository at \c -.URL "https://github.com/sornas/mum/" "" +.URL "https://github.com/mum\-rs/mum/" "" or by e\-mail to \c .MTO "gustav\(atsornas.net" "" "." .SH "SEE ALSO" .sp -mumd(1), mumdrc(5) +mumd(1), mumdrc(5)
\ No newline at end of file diff --git a/documentation/mumctl.txt b/documentation/mumctl.txt index 5b1607c..b9f083f 100644 --- a/documentation/mumctl.txt +++ b/documentation/mumctl.txt @@ -40,15 +40,15 @@ mumctl channel list :: mumctl completions [--bash|--fish|--zsh] :: Generate a completion file for the specified shell. -mumctl config <name> <value> :: - Set a configuration value in the mumd(1) config-file. +mumctl config <key> <value> :: + Set a configuration value in the mumd(1) config-file. See mumdrc(5). mumctl config-reload :: Force a reload of the configuration file (e.g. after editing it externally. -mumctl connect [-p|--port <port>] <host> [username] :: +mumctl connect <host> [-p|--port <port>] [username] :: Connect to a server on the specified port. The host may be either the name - of a saved server or an IP or a URL (in which case username needs to be passed + of a saved server or an IP/URL (in which case username needs to be passed as well). If omitted, the port defaults to 64738. @@ -65,10 +65,10 @@ mumctl mute [user] :: Mute yourself or someone else. If user is omitted, you mute yourself. Otherwise, the user with the username [user] is muted. -mumctl server add [--password <password>] [--port <port>] [--username <username>] [<name>] <host> :: +mumctl server add <name> <host> [--port <port>] [<username> | <username> <password>] :: Add a saved server configuration. -mumctl server config <server> <name> <value> :: +mumctl server config <server> <key> <value> :: Configure a variable in a saved server configuration. mumctl server list :: @@ -106,7 +106,7 @@ Gustav Sörnäs and Eskil Queseth. Reporting bugs -------------- -Please report bugs to the Github repository at https://github.com/sornas/mum/ +Please report bugs to the Github repository at https://github.com/mum-rs/mum/ or by e-mail to gustav@sornas.net. See also diff --git a/documentation/mumd.1 b/documentation/mumd.1 index 9a26720..bedd1cb 100644 --- a/documentation/mumd.1 +++ b/documentation/mumd.1 @@ -2,12 +2,12 @@ .\" Title: mumd .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.12 -.\" Date: 2021-01-07 +.\" Date: 2021-04-03 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "MUMD" "1" "2021-01-07" "\ \&" "\ \&" +.TH "MUMD" "1" "2021-04-03" "\ \&" "\ \&" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 @@ -80,7 +80,7 @@ Gustav Sörnäs and Eskil Queseth. .SH "REPORTING BUGS" .sp Please report bugs to the Github repository at \c -.URL "https://github.com/sornas/mum/" "" +.URL "https://github.com/mum\-rs/mum/" "" or by e\-mail to \c .MTO "gustav\(atsornas.net" "" "." .SH "SEE ALSO" diff --git a/documentation/mumd.txt b/documentation/mumd.txt index 5beb275..67e3555 100644 --- a/documentation/mumd.txt +++ b/documentation/mumd.txt @@ -36,7 +36,7 @@ Gustav Sörnäs and Eskil Queseth. Reporting bugs -------------- -Please report bugs to the Github repository at https://github.com/sornas/mum/ +Please report bugs to the Github repository at https://github.com/mum-rs/mum/ or by e-mail to gustav@sornas.net. See also diff --git a/documentation/mumdrc.5 b/documentation/mumdrc.5 index 88bb748..1e78e5a 100644 --- a/documentation/mumdrc.5 +++ b/documentation/mumdrc.5 @@ -2,12 +2,12 @@ .\" Title: mumdrc .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.12 -.\" Date: 2021-01-07 +.\" Date: 2021-04-03 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "MUMDRC" "5" "2021-01-07" "\ \&" "\ \&" +.TH "MUMDRC" "5" "2021-04-03" "\ \&" "\ \&" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 @@ -79,7 +79,7 @@ Gustav Sörnäs and Eskil Queseth. .SH "REPORTING BUGS" .sp Please report bugs to the Github repository at \c -.URL "https://github.com/sornas/mum/" "" +.URL "https://github.com/mum\-rs/mum/" "" or by e\-mail to \c .MTO "gustav\(atsornas.net" "" "." .SH "SEE ALSO" diff --git a/documentation/mumdrc.txt b/documentation/mumdrc.txt index 5365e06..ed54b87 100644 --- a/documentation/mumdrc.txt +++ b/documentation/mumdrc.txt @@ -46,7 +46,7 @@ Gustav Sörnäs and Eskil Queseth. Reporting bugs -------------- -Please report bugs to the Github repository at https://github.com/sornas/mum/ +Please report bugs to the Github repository at https://github.com/mum-rs/mum/ or by e-mail to gustav@sornas.net. See also diff --git a/mumctl/Cargo.toml b/mumctl/Cargo.toml index 02326c8..f19a67c 100644 --- a/mumctl/Cargo.toml +++ b/mumctl/Cargo.toml @@ -9,13 +9,14 @@ CLI controller for mumd. """ repository = "https://github.com/sornas/mum" license = "MIT" +readme = "../README.md" [dependencies] mumlib = { version = "0.3", path = "../mumlib" } -clap = { version = "2.33", features = ["yaml"] } -colored = "2.0" +bincode = "1" +colored = "2" log = "0.4" -bincode = "1.3.2" +structopt = "0.3" #cursive = "0.15" diff --git a/mumctl/build.rs b/mumctl/build.rs index 0a4f506..c11146a 100644 --- a/mumctl/build.rs +++ b/mumctl/build.rs @@ -11,9 +11,11 @@ fn main() { fn commit_hash() -> Option<String> { let output = Command::new("git") - .arg("describe") - .arg("--tags") - .current_dir(env!("CARGO_MANIFEST_DIR")) - .output(); - output.ok().map(|o| String::from_utf8_lossy(&o.stdout).to_string()) + .arg("describe") + .arg("--tags") + .current_dir(env!("CARGO_MANIFEST_DIR")) + .output(); + output + .ok() + .map(|o| String::from_utf8_lossy(&o.stdout).to_string()) } diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index a187a3a..29c9e44 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -1,21 +1,13 @@ -use clap::{App, AppSettings, Arg, Shell, SubCommand, ArgMatches}; use colored::Colorize; -use log::{Record, Level, Metadata, LevelFilter, error, warn}; -use mumlib::command::{Command, CommandResponse}; -use mumlib::config::{self, ServerConfig, Config}; -use mumlib::state::Channel; -use std::{io::{self, Read, BufRead, Write}, iter, fmt::{Display, Formatter}, os::unix::net::UnixStream}; +use log::*; +use mumlib::command::{Command as MumCommand, CommandResponse}; +use mumlib::config::{self, Config, ServerConfig}; +use mumlib::state::Channel as MumChannel; +use std::{fmt,io::{self, BufRead, Read, Write}, iter, os::unix::net::UnixStream}; +use structopt::{clap::Shell, StructOpt}; const INDENTATION: &str = " "; -macro_rules! error_if_err { - ($func:expr) => { - if let Err(e) = $func { - error!("{}", e); - } - }; -} - struct SimpleLogger; impl log::Log for SimpleLogger { @@ -32,7 +24,8 @@ impl log::Log for SimpleLogger { Level::Warn => "warning: ".yellow(), _ => "".normal(), }, - record.args()); + record.args() + ); } } @@ -41,519 +34,499 @@ impl log::Log for SimpleLogger { static LOGGER: SimpleLogger = SimpleLogger; -fn main() { - log::set_logger(&LOGGER) - .map(|()| log::set_max_level(LevelFilter::Info)).unwrap(); - let mut config = match config::read_default_cfg() { - Ok(c) => c, - Err(e) => { - error!("Couldn't read config: {}", e); - return; - } - }; +#[derive(Debug, StructOpt)] +struct Mum { + #[structopt(subcommand)] + command: Command, +} - let mut app = App::new("mumctl") - .setting(AppSettings::ArgRequiredElseHelp) - .version(env!("VERSION")) - .subcommand( - SubCommand::with_name("connect") - .about("Connect to a server") - .arg(Arg::with_name("host").required(true)) - .arg(Arg::with_name("username")) - .arg(Arg::with_name("password")) - .arg( - Arg::with_name("port") - .long("port") - .short("p") - .takes_value(true), - ), - ) - .subcommand( - SubCommand::with_name("disconnect") - .about("Disconnect from the currently connected server"), - ) - .subcommand( - SubCommand::with_name("server") - .setting(AppSettings::ArgRequiredElseHelp) - .about("Handle servers") - .subcommand( - SubCommand::with_name("config") - .about("Configure a saved server") - .arg(Arg::with_name("server_name")) - .arg(Arg::with_name("var_name")) - .arg(Arg::with_name("var_value")), - ) - .subcommand( - SubCommand::with_name("rename") - .about("Rename a saved server") - .arg(Arg::with_name("prev_name").required(true)) - .arg(Arg::with_name("next_name").required(true)), - ) - .subcommand( - SubCommand::with_name("add") - .about("Add a new saved server") - .arg(Arg::with_name("name").required(true)) - .arg(Arg::with_name("host").required(true)) - .arg( - Arg::with_name("port") - .long("port") - .takes_value(true) - .default_value("64738"), - ) - .arg( - Arg::with_name("username") - .long("username") - .takes_value(true), - ) - .arg( - Arg::with_name("password") - .long("password") - .takes_value(true), - ), - ) - .subcommand( - SubCommand::with_name("remove") - .about("Remove a saved server") - .arg(Arg::with_name("name").required(true)), - ) - .subcommand( - SubCommand::with_name("list") - .about("List saved servers and number of people connected"), - ), - ) - .subcommand( - SubCommand::with_name("channel") - .about("Handle channels in the connected server") - .setting(AppSettings::ArgRequiredElseHelp) - .subcommand( - SubCommand::with_name("list") - .about("List all channels") - .arg(Arg::with_name("short").long("short").short("s")), - ) - .subcommand( - SubCommand::with_name("connect") - .about("Connect to another channel") - .arg(Arg::with_name("channel").required(true)), - ), - ) - .subcommand(SubCommand::with_name("status").about("Show current status")) - .subcommand( - SubCommand::with_name("config") - .about("Change config values") - .arg(Arg::with_name("name").required(true)) - .arg(Arg::with_name("value").required(true)), - ) - .subcommand( - SubCommand::with_name("config-reload").about("Force a reload of the config file"), - ) - .subcommand( - SubCommand::with_name("completions") - .about("Generate CLI completions") - .arg(Arg::with_name("zsh").long("zsh")) - .arg(Arg::with_name("bash").long("bash")) - .arg(Arg::with_name("fish").long("fish")), - ) - .subcommand( - SubCommand::with_name("volume") - .about("Change volume of either you or someone else") - .subcommand( - SubCommand::with_name("set") - .arg(Arg::with_name("user").required(true)) - .arg(Arg::with_name("volume").required(true)), - ) - .arg(Arg::with_name("user").required(true)) - .setting(AppSettings::SubcommandsNegateReqs), - ) - .subcommand( - SubCommand::with_name("mute") - .about("Mute someone/yourself") - .arg(Arg::with_name("user")) - ) - .subcommand( - SubCommand::with_name("unmute") - .about("Unmute someone/yourself") - .arg(Arg::with_name("user")) - ) - .subcommand( - SubCommand::with_name("deafen") - .about("Deafen yourself") - ) - .subcommand( - SubCommand::with_name("undeafen") - .about("Undeafen yourself") - ); +#[derive(Debug, StructOpt)] +enum Command { + /// Connect to a server + Connect { + host: String, + username: Option<String>, + password: Option<String>, + #[structopt(short = "p", long = "port")] + port: Option<u16>, + }, + /// Disconnect from the currently connected server + Disconnect, + /// Handle servers + Server(Server), + /// Handle channels in the connected server + Channel(Channel), + /// Show current status + Status, + /// Change config values + Config { + key: String, + value: String, + }, + /// Reload the config file + ConfigReload, + /// Output CLI completions + Completions(Completions), + /// Change volume of either you or someone else + Volume { + user: String, + volume: Option<f32>, + }, + /// Mute someone/yourself + Mute { + user: Option<String>, + }, + /// Unmute someone/yourself + Unmute { + user: Option<String>, + }, + /// Deafen yourself + Deafen, + /// Undeafen yourself + Undeafen, +} + +#[derive(Debug, StructOpt)] +enum Server { + /// Configure a saved server + Config { + server_name: Option<String>, + key: Option<String>, + value: Option<String>, + }, + /// Rename a saved server + Rename { + old_name: String, + new_name: String + }, + /// Add a new saved server + Add { + name: String, + host: String, + #[structopt(short = "p", long = "port")] + port: Option<u16>, + username: Option<String>, + #[structopt(requires = "username")] + password: Option<String>, + }, + /// Remove a saved server + Remove { + name: String, + }, + /// List saved servers and number of people connected + List, +} - let matches = app.clone().get_matches(); +#[derive(Debug, StructOpt)] +enum Channel { + List { + #[structopt(short = "s", long = "short")] + short: bool, + }, + Connect { + name: String, + }, +} - if let Err(e) = process_matches(matches, &mut config, &mut app) { - error!("{}", e); +#[derive(Debug, StructOpt)] +enum Completions { + Zsh, + Bash, + Fish, +} + +#[derive(Debug)] +enum CliError { + NoUsername, + ConnectionError, + NoServerFound(String), + NotSet(String), + UseServerRename, + ConfigKeyNotFound(String), + ServerAlreadyExists(String), + NoServers, +} + +#[derive(Debug)] +struct Error(Box<dyn std::error::Error>); // new type since otherwise we'd get an impl conflict with impl<T> From<T> for T below + +impl<E> From<E> for Error +where + E: std::error::Error + fmt::Display + 'static, +{ + fn from(e: E) -> Self { + Error(Box::new(e)) } +} - if !config::cfg_exists() { - println!( - "Config file not found. Create one in {}? [Y/n]", - config::default_cfg_path().display(), - ); - let stdin = std::io::stdin(); - let response = stdin.lock().lines().next(); - if let Some(Ok(true)) = response.map(|e| e.map(|e| &e == "Y")) { - error_if_err!(config.write_default_cfg(true)); - } - } else { - error_if_err!(config.write_default_cfg(false)); +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) } } -fn process_matches(matches: ArgMatches, config: &mut Config, app: &mut App) -> Result<(), Error> { - if let Some(matches) = matches.subcommand_matches("connect") { - match_server_connect(matches, &config)?; - } else if let Some(_) = matches.subcommand_matches("disconnect") { - error_if_err!(send_command(Command::ServerDisconnect)?); - } else if let Some(matches) = matches.subcommand_matches("server") { - if let Some(matches) = matches.subcommand_matches("config") { - match_server_config(matches, config); - } else if let Some(matches) = matches.subcommand_matches("rename") { - match_server_rename(matches, config); - } else if let Some(matches) = matches.subcommand_matches("remove") { - match_server_remove(matches, config); - } else if let Some(matches) = matches.subcommand_matches("add") { - match_server_add(matches, config); - } else if let Some(_) = matches.subcommand_matches("list") { - if config.servers.is_empty() { - warn!("No servers in config"); +impl std::error::Error for CliError {} + +impl fmt::Display for CliError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CliError::NoUsername => { + write!(f, "No username specified") } - let query = config - .servers - .iter() - .map(|e| { - let response = send_command(Command::ServerStatus { - host: e.host.clone(), - port: e.port.unwrap_or(mumlib::DEFAULT_PORT), - }); - response.map(|f| (e, f)) - }) - .collect::<Result<Vec<_>, _>>()?; - for (server, response) in query.into_iter() - .filter(|e| e.1.is_ok()) - .map(|e| (e.0, e.1.unwrap().unwrap())) { - if let CommandResponse::ServerStatus { - users, max_users, .. - } = response - { - println!("{} [{}/{}]", server.name, users, max_users) - } else { - unreachable!() - } + CliError::ConnectionError => { + write!(f, "Unable to connect to mumd. Is mumd running?") + } + CliError::NoServerFound(s) => { + write!(f, "Server '{}' not found", s) + } + CliError::NotSet(s) => { + write!(f, "Key '{}' not set", s) + } + CliError::UseServerRename => { + write!(f, "Use 'server rename' instead") + } + CliError::ConfigKeyNotFound(s) => { + write!(f, "Key '{}' not found", s) + } + CliError::ServerAlreadyExists(s) => { + write!(f, "A server named '{}' already exists", s) + } + CliError::NoServers => { + write!(f, "No servers found") } } - } else if let Some(matches) = matches.subcommand_matches("channel") { - if let Some(_matches) = matches.subcommand_matches("list") { - match send_command(Command::ChannelList)? { - Ok(res) => match res { - Some(CommandResponse::ChannelList { channels }) => { - print_channel(&channels, 0); - } - _ => unreachable!(), - }, - Err(e) => error!("{}", e), + } +} + +fn main() { + log::set_logger(&LOGGER) + .map(|()| log::set_max_level(LevelFilter::Info)) + .unwrap(); + if let Err(e) = match_opt() { + error!("{}", e); + } +} +fn match_opt() -> Result<(), Error> { + let mut config = config::read_default_cfg()?; + + let opt = Mum::from_args(); + match opt.command { + Command::Connect { + host, + username, + password, + port, + } => { + let port = port.unwrap_or(mumlib::DEFAULT_PORT); + + let (host, username, password, port) = + match config.servers.iter().find(|e| e.name == host) { + Some(server) => ( + &server.host, + server + .username + .as_ref() + .or(username.as_ref()) + .ok_or(CliError::NoUsername)?, + server.password.as_ref().or(password.as_ref()), + server.port.unwrap_or(port), + ), + None => ( + &host, + username.as_ref().ok_or(CliError::NoUsername)?, + password.as_ref(), + port, + ), + }; + + let response = send_command(MumCommand::ServerConnect { + host: host.to_string(), + port, + username: username.to_string(), + password: password.map(|x| x.to_string()), + accept_invalid_cert: true, //TODO + })??; + if let Some(CommandResponse::ServerConnect { welcome_message }) = response { + println!("Connected to {}", host); + if let Some(message) = welcome_message { + println!("Welcome: {}", message); + } } - } else if let Some(matches) = matches.subcommand_matches("connect") { - error_if_err!(send_command(Command::ChannelJoin { - channel_identifier: matches.value_of("channel").unwrap().to_string() - })); } - } else if let Some(_) = matches.subcommand_matches("status") { - match send_command(Command::Status)? { - Ok(res) => match res { - Some(CommandResponse::Status { server_state }) => { - parse_status(&server_state); + Command::Disconnect => { + send_command(MumCommand::ServerDisconnect)??; + } + Command::Server(server_command) => { + match_server_command(server_command, &mut config)?; + } + Command::Channel(channel_command) => { + match channel_command { + Channel::List { short: _short } => { + //TODO actually use this + match send_command(MumCommand::ChannelList)?? { + Some(CommandResponse::ChannelList { channels }) => { + print_channel(&channels, 0); + } + _ => unreachable!("Response should only be a ChannelList"), + } + } + Channel::Connect { name } => { + send_command(MumCommand::ChannelJoin { + channel_identifier: name, + })??; } - _ => unreachable!(), - }, - Err(e) => error!("{}", e), + } } - } else if let Some(matches) = matches.subcommand_matches("config") { - let name = matches.value_of("name").unwrap(); - let value = matches.value_of("value").unwrap(); - match name { + Command::Status => match send_command(MumCommand::Status)?? { + Some(CommandResponse::Status { server_state }) => { + parse_state(&server_state); + } + _ => unreachable!("Response should only be a Status"), + }, + Command::Config { key, value } => match key.as_str() { "audio.input_volume" => { if let Ok(volume) = value.parse() { - error_if_err!(send_command(Command::InputVolumeSet(volume))?); + send_command(MumCommand::InputVolumeSet(volume))??; config.audio.input_volume = Some(volume); } } "audio.output_volume" => { if let Ok(volume) = value.parse() { - error_if_err!(send_command(Command::OutputVolumeSet(volume))?); + send_command(MumCommand::OutputVolumeSet(volume))??; config.audio.output_volume = Some(volume); } } _ => { - error!("unknown config value {}", name); + return Err(CliError::ConfigKeyNotFound(key))?; } + }, + Command::ConfigReload => { + send_command(MumCommand::ConfigReload)??; } - } else if matches.subcommand_matches("config-reload").is_some() { - error_if_err!(send_command(Command::ConfigReload)?); - } else if let Some(matches) = matches.subcommand_matches("completions") { - app.gen_completions_to( - "mumctl", - match matches.value_of("shell").unwrap_or("zsh") { - "bash" => Shell::Bash, - "fish" => Shell::Fish, - _ => Shell::Zsh, - }, - &mut io::stdout(), - ); - } else if let Some(matches) = matches.subcommand_matches("volume") { - if let Some(matches) = matches.subcommand_matches("set") { - let user = matches.value_of("user").unwrap(); - let volume = matches.value_of("volume").unwrap(); - if let Ok(val) = volume.parse() { - error_if_err!(send_command(Command::UserVolumeSet(user.to_string(), val))) + Command::Completions(completions) => { + Mum::clap().gen_completions_to( + "mumctl", + match completions { + Completions::Bash => Shell::Bash, + Completions::Fish => Shell::Fish, + _ => Shell::Zsh, + }, + &mut io::stdout(), + ); + } + Command::Volume { user, volume } => { + if let Some(volume) = volume { + send_command(MumCommand::UserVolumeSet(user, volume))??; } else { - error!("invalid volume value: {}", volume); + //TODO report volume of user + // needs work on mumd + todo!(); } - } else { - let _user = matches.value_of("user").unwrap(); - //TODO implement me - //needs work on mumd to implement } - } else if let Some(matches) = matches.subcommand_matches("mute") { - let command = if let Some(user) = matches.value_of("user") { - Command::MuteOther(user.to_string(), Some(true)) - } else { - Command::MuteSelf(Some(true)) - }; - if let Err(e) = send_command(command)? { - error!("{}", e); - } - } else if let Some(matches) = matches.subcommand_matches("unmute") { - let command = if let Some(user) = matches.value_of("user") { - Command::MuteOther(user.to_string(), Some(false)) - } else { - Command::MuteSelf(Some(false)) - }; - if let Err(e) = send_command(command)? { - error!("{}", e); + Command::Mute { user } => match user { + Some(user) => { + send_command(MumCommand::MuteOther(user, Some(true)))??; + } + None => { + send_command(MumCommand::MuteSelf(Some(true)))??; + } + }, + Command::Unmute { user } => match user { + Some(user) => { + send_command(MumCommand::MuteOther(user, Some(false)))??; + } + None => { + send_command(MumCommand::MuteSelf(Some(false)))??; + } + }, + Command::Deafen => { + send_command(MumCommand::DeafenSelf(Some(true)))??; } - } else if let Some(_) = matches.subcommand_matches("deafen") { - if let Err(e) = send_command(Command::DeafenSelf(Some(true)))? { - error!("{}", e); + Command::Undeafen => { + send_command(MumCommand::DeafenSelf(Some(false)))??; } - } else if let Some(_) = matches.subcommand_matches("undeafen") { - if let Err(e) = send_command(Command::DeafenSelf(Some(false)))? { - error!("{}", e); + } + + if !config::cfg_exists() { + println!( + "Config file not found. Create one in {}? [Y/n]", + config::default_cfg_path().display(), + ); + let stdin = std::io::stdin(); + let response = stdin.lock().lines().next(); + if let Some(Ok(true)) = response.map(|e| e.map(|e| &e == "Y")) { + config.write_default_cfg(true)?; } } else { - unreachable!(); + config.write_default_cfg(false)?; } - Ok(()) } -fn match_server_connect(matches: &clap::ArgMatches<'_>, config: &mumlib::config::Config) -> Result<(), Error> { - let host = matches.value_of("host").unwrap(); - let username = matches.value_of("username"); - let password = matches.value_of("password"); - let port = match matches.value_of("port").map(|e| e.parse()) { - None => Some(mumlib::DEFAULT_PORT), - Some(Err(_)) => None, - Some(Ok(v)) => Some(v), - }; - if let Some(port) = port { - let (host, port, username, password) = match config.servers.iter().find(|e| e.name == host) { - Some(server_config) => { - let host = server_config.host.as_str(); - let port = server_config.port.unwrap_or(port); - let username = server_config - .username - .as_deref() - .or(username); - if username.is_none() { - error!("no username specified"); - return Ok(()); //TODO? return as error - } - let password = server_config - .password - .as_deref() - .or(password); - (host, port, username.unwrap(), password) - } - None => { - if username.is_none() { - error!("no username specified"); - return Ok(()); //TODO? return as error - } - (host, port, username.unwrap(), password) - } - }; - let response = send_command(Command::ServerConnect { - host: host.to_string(), - port, - username: username.to_string(), - password: password.map(|x| x.to_string()), - accept_invalid_cert: true, //TODO - })? - .map(|e| (e, host)); - match response { - Ok((e, host)) => { - if let Some(CommandResponse::ServerConnect { welcome_message }) = e { - println!("Connected to {}", host); - if let Some(message) = welcome_message { - println!("Welcome: {}", message); +fn match_server_command(server_command: Server, config: &mut Config) -> Result<(), CliError> { + match server_command { + Server::Config { + server_name, + key, + value, + } => { + let server_name = match server_name { + Some(server_name) => server_name, + None => { + for server in config.servers.iter() { + println!("{}", server.name); } + return Ok(()); } - } - Err(e) => { - error!("{}", e); - } - }; - } - - Ok(()) -} + }; + let server = config + .servers + .iter_mut() + .find(|s| s.name == server_name) + .ok_or(CliError::NoServerFound(server_name))?; -fn match_server_config(matches: &clap::ArgMatches<'_>, config: &mut mumlib::config::Config) { - if let Some(server_name) = matches.value_of("server_name") { - let server = config.servers.iter_mut().find(|s| s.name == server_name); - if let Some(server) = server { - if let Some(var_name) = matches.value_of("var_name") { - if let Some(var_value) = matches.value_of("var_value") { - // save var_value in var_name (if it is valid) - match var_name { - "name" => { - error!("use mumctl server rename instead!"); - } - "host" => { - server.host = var_value.to_string(); - } - "port" => { - server.port = Some(var_value.parse().unwrap()); - } - "username" => { - server.username = Some(var_value.to_string()); - } - "password" => { - server.password = Some(var_value.to_string()); //TODO ask stdin if empty - } - _ => { - error!("variable {} not found", var_name); - } - }; - } else { - // var_value is None - // print value of var_name + match (key.as_deref(), value) { + (None, _) => { + print!( + "{}{}{}{}", + format!("host: {}\n", server.host.to_string()), + server + .port + .map(|s| format!("port: {}\n", s)) + .unwrap_or_else(|| "".to_string()), + server + .username + .as_ref() + .map(|s| format!("username: {}\n", s)) + .unwrap_or_else(|| "".to_string()), + server + .password + .as_ref() + .map(|s| format!("password: {}\n", s)) + .unwrap_or_else(|| "".to_string()), + ); + } + (Some("name"), None) => { + println!("{}", server.name); + } + (Some("host"), None) => { + println!("{}", server.host); + } + (Some("port"), None) => { println!( "{}", - match var_name { - "name" => { - server.name.to_string() - } - "host" => { - server.host.to_string() - } - "port" => { - server - .port - .map(|s| s.to_string()) - .unwrap_or(format!("{} not set", "error:".red())) - } - "username" => { - server - .username - .as_ref() - .map(|s| s.to_string()) - .unwrap_or(format!("{} not set", "error:".red())) - } - "password" => { - server - .password - .as_ref() - .map(|s| s.to_string()) - .unwrap_or(format!("{} not set", "error:".red())) - } - _ => { - format!("{} unknown variable", "error:".red()) - } - } + server.port.ok_or(CliError::NotSet("port".to_string()))? ); } - } else { - // var_name is None - // print server config - print!( - "{}{}{}{}", - format!("host: {}\n", server.host.to_string()), - server - .port - .map(|s| format!("port: {}\n", s)) - .unwrap_or_else(|| "".to_string()), - server - .username - .as_ref() - .map(|s| format!("username: {}\n", s)) - .unwrap_or_else(|| "".to_string()), - server - .password - .as_ref() - .map(|s| format!("password: {}\n", s)) - .unwrap_or_else(|| "".to_string()), - ) + (Some("username"), None) => { + println!( + "{}", + server + .username + .as_ref() + .ok_or(CliError::NotSet("username".to_string()))? + ); + } + (Some("password"), None) => { + println!( + "{}", + server + .password + .as_ref() + .ok_or(CliError::NotSet("password".to_string()))? + ); + } + (Some("name"), Some(_)) => { + return Err(CliError::UseServerRename)?; + } + (Some("host"), Some(value)) => { + server.host = value; + } + (Some("port"), Some(value)) => { + server.port = Some(value.parse().unwrap()); + } + (Some("username"), Some(value)) => { + server.username = Some(value); + } + (Some("password"), Some(value)) => { + server.password = Some(value); + //TODO ask stdin if empty + } + (Some(_), _) => { + return Err(CliError::ConfigKeyNotFound(key.unwrap()))?; + } } - } else { - // server is None - error!("server {} not found", server_name); - } - } else { - for server in config.servers.iter() { - println!("{}", server.name); } - } -} - -fn match_server_rename(matches: &clap::ArgMatches<'_>, config: &mut mumlib::config::Config) { - let prev_name = matches.value_of("prev_name").unwrap(); - let next_name = matches.value_of("next_name").unwrap(); - if let Some(server) = config.servers.iter_mut().find(|s| s.name == prev_name) { - server.name = next_name.to_string(); - } else { - error!("server {} not found", prev_name); - } -} - -fn match_server_remove(matches: &clap::ArgMatches<'_>, config: &mut mumlib::config::Config) { - let name = matches.value_of("name").unwrap(); - match config.servers.iter().position(|server| server.name == name) { - Some(idx) => { - config.servers.remove(idx); - } - None => { - error!("server {} not found", name); + Server::Rename { old_name, new_name } => { + config + .servers + .iter_mut() + .find(|s| s.name == old_name) + .ok_or(CliError::NoServerFound(old_name))? + .name = new_name; } - }; -} - -fn match_server_add(matches: &clap::ArgMatches<'_>, config: &mut mumlib::config::Config) { - let name = matches.value_of("name").unwrap().to_string(); - let host = matches.value_of("host").unwrap().to_string(); - // optional arguments map None to None - let port = matches.value_of("port").map(|s| s.parse().unwrap()); - let username = matches.value_of("username").map(|s| s.to_string()); - let password = matches.value_of("password").map(|s| s.to_string()); - if config.servers.iter().any(|s| s.name == name) { - error!("a server named {} already exists", name); - } else { - config.servers.push(ServerConfig { + Server::Add { name, host, port, username, password, - }); + } => { + if config.servers.iter().any(|s| s.name == name) { + return Err(CliError::ServerAlreadyExists(name))?; + } else { + config.servers.push(ServerConfig { + name, + host, + port, + username, + password, + }); + } + } + Server::Remove { name } => { + let idx = config + .servers + .iter() + .position(|s| s.name == name) + .ok_or(CliError::NoServerFound(name))?; + config.servers.remove(idx); + } + Server::List => { + if config.servers.is_empty() { + return Err(CliError::NoServers)?; + } + let query = config + .servers + .iter() + .map(|e| { + let response = send_command(MumCommand::ServerStatus { + host: e.host.clone(), + port: e.port.unwrap_or(mumlib::DEFAULT_PORT), + }); + response.map(|f| (e, f)) + }) + .collect::<Result<Vec<_>, _>>()?; + for (server, response) in query + .into_iter() + .filter(|e| e.1.is_ok()) + .map(|e| (e.0, e.1.unwrap().unwrap())) + { + if let CommandResponse::ServerStatus { + users, max_users, .. + } = response + { + println!("{} [{}/{}]", server.name, users, max_users) + } else { + unreachable!() + } + } + } } + Ok(()) } -fn parse_status(server_state: &mumlib::state::Server) { +fn parse_state(server_state: &mumlib::state::Server) { println!( "Connected to {} as {}", server_state.host, server_state.username @@ -579,19 +552,28 @@ fn parse_status(server_state: &mumlib::state::Server) { } } -fn send_command(command: Command) -> Result<mumlib::error::Result<Option<CommandResponse>>, crate::Error> { - let mut connection = UnixStream::connect(mumlib::SOCKET_PATH).map_err(|_| Error::ConnectionError)?; +fn send_command( + command: MumCommand, +) -> Result<mumlib::error::Result<Option<CommandResponse>>, CliError> { + let mut connection = + UnixStream::connect(mumlib::SOCKET_PATH).map_err(|_| CliError::ConnectionError)?; let serialized = bincode::serialize(&command).unwrap(); - connection.write(&(serialized.len() as u32).to_be_bytes()).map_err(|_| Error::ConnectionError)?; - connection.write(&serialized).map_err(|_| Error::ConnectionError)?; + connection + .write(&(serialized.len() as u32).to_be_bytes()) + .map_err(|_| CliError::ConnectionError)?; + connection + .write(&serialized) + .map_err(|_| CliError::ConnectionError)?; - connection.read_exact(&mut [0; 4]).map_err(|_| Error::ConnectionError)?; - bincode::deserialize_from(&mut connection).map_err(|_| Error::ConnectionError) + connection + .read_exact(&mut [0; 4]) + .map_err(|_| CliError::ConnectionError)?; + bincode::deserialize_from(&mut connection).map_err(|_| CliError::ConnectionError) } -fn print_channel(channel: &Channel, depth: usize) { +fn print_channel(channel: &MumChannel, depth: usize) { println!( "{}{}{}", iter::repeat(INDENTATION).take(depth).collect::<String>(), @@ -615,14 +597,3 @@ fn print_channel(channel: &Channel, depth: usize) { print_channel(child, depth + 1); } } - -#[derive(Debug)] -enum Error { - ConnectionError -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "Unable to connect to mumd. Is mumd running?") - } -} diff --git a/mumd/Cargo.toml b/mumd/Cargo.toml index a853622..9d7eeaa 100644 --- a/mumd/Cargo.toml +++ b/mumd/Cargo.toml @@ -9,6 +9,7 @@ Mumble client daemon. """ repository = "https://github.com/sornas/mum" license = "MIT" +readme = "../README.md" [features] default = ["notifications"] diff --git a/mumd/src/network/udp.rs b/mumd/src/network/udp.rs index 3ca77af..086e072 100644 --- a/mumd/src/network/udp.rs +++ b/mumd/src/network/udp.rs @@ -33,7 +33,11 @@ pub async fn handle( mut connection_info_receiver: watch::Receiver<Option<ConnectionInfo>>, mut crypt_state_receiver: mpsc::Receiver<ClientCryptState>, ) -> Result<(), UdpError> { +<<<<<<< HEAD let receiver = state.read().unwrap().audio_input().receiver(); +======= + let receiver = state.read().unwrap().audio().input_receiver(); +>>>>>>> main loop { let connection_info = 'data: loop { @@ -151,7 +155,11 @@ async fn listen( state .read() .unwrap() +<<<<<<< HEAD .audio_output() +======= + .audio() +>>>>>>> main .decode_packet_payload(VoiceStreamType::UDP, session_id, payload); } } diff --git a/mumlib/Cargo.toml b/mumlib/Cargo.toml index 43bd8c5..245b108 100644 --- a/mumlib/Cargo.toml +++ b/mumlib/Cargo.toml @@ -9,11 +9,12 @@ Exposed parts of mum. """ repository = "https://github.com/sornas/mum" license = "MIT" +readme = "../README.md" [dependencies] -colored = "2.0" +colored = "2" dirs = "3" fern = "0.6" log = "0.4" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1", features = ["derive"] } toml = "0.5" diff --git a/mumlib/src/error.rs b/mumlib/src/error.rs index 6b7dccd..459ede0 100644 --- a/mumlib/src/error.rs +++ b/mumlib/src/error.rs @@ -13,6 +13,8 @@ pub enum Error { InvalidServerPassword, } +impl std::error::Error for Error {} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -43,6 +45,9 @@ impl fmt::Display for ChannelIdentifierError { } } +impl std::error::Error for ChannelIdentifierError {} + +#[derive(Debug)] pub enum ConfigError { InvalidConfig, TOMLErrorSer(toml::ser::Error), @@ -52,6 +57,8 @@ pub enum ConfigError { IOError(std::io::Error), } +impl std::error::Error for ConfigError {} + impl fmt::Display for ConfigError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { |
