aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2021-04-06 20:43:11 +0200
committerGitHub <noreply@github.com>2021-04-06 20:43:11 +0200
commit06a765afc73ec1f8f2af27f4ea2730ddaaf05852 (patch)
tree43607aab322996c4ff4ab6bf06a1345c98b98297
parent1734a72d3caff4f8831f4b366fdb818fddecf32f (diff)
parentcd35ea952d0615793c08ad2c07f5d1245ab8f28f (diff)
downloadmum-06a765afc73ec1f8f2af27f4ea2730ddaaf05852.tar.gz
Merge pull request #81 from mum-rs/structopt
-rw-r--r--Cargo.lock145
-rw-r--r--documentation/mumctl.120
-rw-r--r--documentation/mumctl.txt14
-rw-r--r--documentation/mumd.16
-rw-r--r--documentation/mumd.txt2
-rw-r--r--documentation/mumdrc.56
-rw-r--r--documentation/mumdrc.txt2
-rw-r--r--mumctl/Cargo.toml6
-rw-r--r--mumctl/build.rs12
-rw-r--r--mumctl/src/main.rs949
-rw-r--r--mumlib/Cargo.toml4
-rw-r--r--mumlib/src/error.rs7
12 files changed, 591 insertions, 582 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 93f7118..2f883ed 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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..621b1d0 100644
--- a/mumctl/Cargo.toml
+++ b/mumctl/Cargo.toml
@@ -13,9 +13,9 @@ license = "MIT"
[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/mumlib/Cargo.toml b/mumlib/Cargo.toml
index 43bd8c5..183ebac 100644
--- a/mumlib/Cargo.toml
+++ b/mumlib/Cargo.toml
@@ -11,9 +11,9 @@ repository = "https://github.com/sornas/mum"
license = "MIT"
[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 {