implement command line and config parsing
authorGeoffrey Allott <geoffrey@allott.email>
Sat, 6 Mar 2021 22:24:02 +0000 (22:24 +0000)
committerGeoffrey Allott <geoffrey@allott.email>
Sat, 6 Mar 2021 22:24:02 +0000 (22:24 +0000)
Cargo.lock
Cargo.toml
pokerwave.toml [new file with mode: 0644]
src/config.rs [new file with mode: 0644]
src/dealer.rs
src/main.rs
src/pubsub.rs

index e575ad0499b03ab35825239c1673cb7d4f84e6ac..37301c4cc43a550b7a0f5646a246154b906c1fd4 100644 (file)
@@ -63,6 +63,15 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "anyhow"
 version = "1.0.38"
@@ -186,6 +195,18 @@ dependencies = [
  "event-listener",
 ]
 
+[[package]]
+name = "async-native-tls"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e9e7a929bd34c68a82d58a4de7f86fffdaf97fb2af850162a7bb19dd7269b33"
+dependencies = [
+ "async-std",
+ "native-tls",
+ "thiserror",
+ "url",
+]
+
 [[package]]
 name = "async-process"
 version = "1.0.2"
@@ -329,6 +350,12 @@ version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
 
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
 [[package]]
 name = "block-buffer"
 version = "0.9.0"
@@ -419,6 +446,21 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "clap"
+version = "2.33.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
 [[package]]
 name = "combine"
 version = "4.5.2"
@@ -464,6 +506,22 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "core-foundation"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
+
 [[package]]
 name = "cpuid-bool"
 version = "0.1.2"
@@ -615,6 +673,21 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
 [[package]]
 name = "form_urlencoded"
 version = "1.0.1"
@@ -991,6 +1064,24 @@ version = "2.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
 
+[[package]]
+name = "native-tls"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
 [[package]]
 name = "nb-connect"
 version = "1.0.3"
@@ -1023,6 +1114,39 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
+[[package]]
+name = "openssl"
+version = "0.10.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70"
+dependencies = [
+ "bitflags",
+ "cfg-if 1.0.0",
+ "foreign-types",
+ "lazy_static",
+ "libc",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6"
+dependencies = [
+ "autocfg",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
 [[package]]
 name = "parking"
 version = "2.0.0"
@@ -1073,11 +1197,18 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
+[[package]]
+name = "pkg-config"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+
 [[package]]
 name = "pokerwave"
 version = "0.1.0"
 dependencies = [
  "async-std",
+ "clap",
  "env_logger",
  "futures",
  "getrandom 0.2.2",
@@ -1096,6 +1227,7 @@ dependencies = [
  "tide",
  "tide-rustls",
  "tide-websockets",
+ "toml",
 ]
 
 [[package]]
@@ -1245,6 +1377,7 @@ version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eeb8f8d059ead7805e171fc22de8348a3d611c0f985aaa4f5cf6c0dfc7645407"
 dependencies = [
+ "async-native-tls",
  "async-std",
  "async-trait",
  "bytes 1.0.1",
@@ -1252,6 +1385,7 @@ dependencies = [
  "dtoa",
  "futures-util",
  "itoa",
+ "native-tls",
  "percent-encoding",
  "pin-project-lite 0.2.6",
  "sha1",
@@ -1260,6 +1394,15 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "regex"
 version = "1.4.3"
@@ -1278,6 +1421,15 @@ version = "0.6.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
 
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "ring"
 version = "0.16.20"
@@ -1327,6 +1479,16 @@ version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
 
+[[package]]
+name = "schannel"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
+dependencies = [
+ "lazy_static",
+ "winapi",
+]
+
 [[package]]
 name = "sct"
 version = "0.6.0"
@@ -1337,6 +1499,29 @@ dependencies = [
  "untrusted",
 ]
 
+[[package]]
+name = "security-framework"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dfd318104249865096c8da1dfabf09ddbb6d0330ea176812a62ec75e40c4166"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee48cdde5ed250b0d3252818f646e174ab414036edb884dde62d80a3ac6082d"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
 [[package]]
 name = "semver"
 version = "0.9.0"
@@ -1566,6 +1751,12 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
 
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
 [[package]]
 name = "subtle"
 version = "2.4.0"
@@ -1589,6 +1780,20 @@ dependencies = [
  "unicode-xid",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "rand 0.8.3",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
 [[package]]
 name = "termcolor"
 version = "1.1.2"
@@ -1598,6 +1803,15 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
 [[package]]
 name = "thiserror"
 version = "1.0.24"
@@ -1759,6 +1973,15 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "toml"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "tungstenite"
 version = "0.11.1"
@@ -1802,6 +2025,12 @@ dependencies = [
  "tinyvec",
 ]
 
+[[package]]
+name = "unicode-width"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+
 [[package]]
 name = "unicode-xid"
 version = "0.2.1"
@@ -1853,12 +2082,24 @@ dependencies = [
  "sval",
 ]
 
+[[package]]
+name = "vcpkg"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
+
 [[package]]
 name = "vec-arena"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d"
 
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
 [[package]]
 name = "version_check"
 version = "0.9.2"
index 6b29ef557e6041d09bcdca222f4b4f8d613d1f25..37eda431f99fccd2cef18b486926dd71dbbf5ff1 100644 (file)
@@ -6,6 +6,7 @@ edition = "2018"
 
 [dependencies]
 async-std = { version = "1", features = ["attributes"] }
+clap = "2"
 env_logger = "0.8"
 log = "0.4"
 futures = "0.3"
@@ -15,7 +16,7 @@ itertools = "0.10"
 pin-project = "1.0"
 rand = "0.8"
 rand_chacha = "0.3"
-redis = { version = "0.20", features = ["async-std-comp"] }
+redis = { version = "0.20", features = ["async-std-tls-comp"] }
 serde = "1"
 serde_derive = "1"
 serde_json = "1"
@@ -24,3 +25,4 @@ signal-hook-async-std = "0.2"
 tide = { version = "0.16.0", default-features = false, features = ["h1-server"] }
 tide-rustls = "0.2"
 tide-websockets = "0.3"
+toml = "0.5"
diff --git a/pokerwave.toml b/pokerwave.toml
new file mode 100644 (file)
index 0000000..c6e98ba
--- /dev/null
@@ -0,0 +1,12 @@
+[log]
+filter = "info"
+style = "auto"
+
+[redis]
+#addr = "redis://pokerwave:59f278cd2a96756d67884976f54c1e490d00cb4f7ff62c24a380680e5f5098a3@localhost/0"
+
+[server]
+bind = ["localhost:4343"]
+site = "site"
+cert = "cert/cert.pem"
+key = "cert/key.pem"
diff --git a/src/config.rs b/src/config.rs
new file mode 100644 (file)
index 0000000..86604fe
--- /dev/null
@@ -0,0 +1,75 @@
+use std::path::PathBuf;
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct Config {
+    #[serde(default)]
+    pub log: LogConfig,
+    #[serde(default)]
+    pub redis: RedisConfig,
+    #[serde(default)]
+    pub server: ServerConfig,
+}
+
+fn default_filter() -> String {
+    "warn".to_string()
+}
+
+fn default_style() -> String {
+    "auto".to_string()
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct LogConfig {
+    #[serde(default = "default_filter")]
+    pub filter: String,
+    #[serde(default = "default_style")]
+    pub style: String,
+}
+
+impl Default for LogConfig {
+    fn default() -> Self {
+        Self { filter: default_filter(), style: default_style() }
+    }
+}
+
+fn default_redis_addr() -> String {
+    "redis://localhost".to_string()
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct RedisConfig {
+    #[serde(default = "default_redis_addr")]
+    pub addr: String,
+}
+
+impl Default for RedisConfig {
+    fn default() -> Self {
+        Self { addr: default_redis_addr() }
+    }
+}
+
+fn default_bind_addr() -> Vec<String> {
+    vec!["localhost:8080".to_string()]
+}
+
+fn default_site_path() -> PathBuf {
+    PathBuf::from("site")
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct ServerConfig {
+    #[serde(default = "default_bind_addr")]
+    pub bind: Vec<String>,
+    #[serde(default = "default_site_path")]
+    pub site: PathBuf,
+    #[serde(default)]
+    pub cert: Option<String>,
+    #[serde(default)]
+    pub key: Option<String>,
+}
+
+impl Default for ServerConfig {
+    fn default() -> Self {
+        Self { bind: default_bind_addr(), site: default_site_path(), cert: None, key: None }
+    }
+}
index e43997e14b5524aa6d611526a1889bfef18a32b4..61ba1cafc0f6592a15c80eb38ea53b8d046d2a6a 100644 (file)
@@ -1,4 +1,6 @@
 use std::collections::HashSet;
+use std::fmt::{self, Display};
+use std::str::FromStr;
 
 use async_std::task::spawn;
 use futures::{channel::mpsc::Receiver, StreamExt};
@@ -19,6 +21,46 @@ struct DealerState {
     game: Box<dyn Game>,
 }
 
+#[derive(Copy, Clone, Debug)]
+pub struct Partition {
+    n: i64,
+    m: i64,
+}
+
+impl Partition {
+    fn start_dealer_for(&self, id: i64) -> bool {
+        id.rem_euclid(self.m) == self.n - 1
+    }
+}
+
+impl FromStr for Partition {
+    type Err = String;
+    fn from_str(str: &str) -> Result<Self, Self::Err> {
+        if let [n, m] = *str.split('/').collect::<Vec<_>>() {
+            match (n.parse::<i64>(), m.parse::<i64>()) {
+                (Ok(n), Ok(m)) if n >= 1 && m >= n => Ok(Partition { n, m }),
+                (Ok(n), Ok(m)) if n < 1 => Err(format!("The partition number N must be >= 1; got {}/{}", n, m)),
+                (Ok(n), Ok(m)) => Err(format!("The partition number N must be <= total partitions M; got {}/{}", n, m)),
+                _ => Err(format!("The partition must be expressed as two integers N/M; got {:?}", str)),
+            }
+        } else {
+            Err(format!("The partition must be expressed in the form N/M; got {:?}", str))
+        }
+    }
+}
+
+impl Display for Partition {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}/{}", self.n, self.m)
+    }
+}
+
+impl Default for Partition {
+    fn default() -> Self {
+        Partition { n: 1, m: 1 }
+    }
+}
+
 enum Termination {
     Continue,
     Break,
@@ -104,7 +146,7 @@ impl Dealer {
     }
 }
 
-pub async fn spawn_dealers(server: Server) -> Result<(), std::io::Error> {
+pub async fn spawn_dealers(server: Server, partition: Partition) -> Result<(), std::io::Error> {
     let (mut server_state, mut update_stream) = server.new_state().await;
     let mut interests = HashSet::new();
     interests.insert(ClientInterest::GameList);
@@ -118,10 +160,14 @@ pub async fn spawn_dealers(server: Server) -> Result<(), std::io::Error> {
                     info!("Starting new game {:?}", game);
                     let id = game.id();
                     game_list.push(game);
-                    let (server_state, update_stream) = server.new_state().await;
-                    if let Ok(dealer) = Dealer::new(server_state, id).await {
-                        info!("Spawning new dealer for game {}", id);
-                        spawn(dealer.start(update_stream));
+                    if partition.start_dealer_for(id) {
+                        let (server_state, update_stream) = server.new_state().await;
+                        if let Ok(dealer) = Dealer::new(server_state, id).await {
+                            info!("Spawning new dealer for game {}", id);
+                            spawn(dealer.start(update_stream));
+                        }
+                    } else {
+                        debug!("Not spawning new dealer for game {} for partition {}", id, partition);
                     }
                 }
             }
index e251ad823d979d1ab09c018e6bcf504930764966..6ddfbc2a1aa1898b1d28c5db44677c6d23666c58 100644 (file)
@@ -3,19 +3,28 @@ extern crate log;
 #[macro_use]
 extern crate serde_derive;
 
-use futures::{channel::mpsc::channel, future::select, pin_mut, FutureExt, StreamExt};
+use std::fs::read_to_string;
+use std::future::Future;
+use std::pin::Pin;
+
+use clap::{app_from_crate, crate_authors, crate_description, crate_name, crate_version, AppSettings, Arg, SubCommand};
+use futures::{channel::mpsc::channel, future::pending, select, FutureExt, StreamExt};
 use redis::Client;
 use signal_hook::consts::signal::*;
 use signal_hook_async_std::Signals;
 use tide::utils::After;
-use tide::{Body, Error, Response, StatusCode};
-//use tide_rustls::TlsListener;
+use tide::{
+    listener::{ConcurrentListener, ToListener},
+    Body, Response, StatusCode,
+};
+use tide_rustls::TlsListener;
 use tide_websockets::WebSocket;
 
 mod api;
 mod auth;
 mod card;
 mod client;
+mod config;
 mod dealer;
 mod game;
 mod pubsub;
@@ -26,17 +35,17 @@ mod username;
 mod util;
 
 use crate::client::new_client;
-use crate::dealer::spawn_dealers;
+use crate::config::Config;
+use crate::dealer::{spawn_dealers, Partition};
 use crate::pubsub::handle_client_interest_subscriptions;
 use crate::server::Server;
 
-async fn handle_signals(mut signals: Signals) -> Result<(), std::io::Error> {
+async fn handle_signals(mut signals: Signals) {
     signals.next().await;
     info!("Shutting down...");
-    Ok(())
 }
 
-async fn serve_404(response: Response) -> Result<Response, Error> {
+async fn serve_404(response: Response) -> Result<Response, tide::Error> {
     match response.status() {
         StatusCode::NotFound => Ok(Response::builder(404).body(Body::from_file("site/404.html").await?).content_type("text/html").build()),
         _ => Ok(response),
@@ -44,41 +53,92 @@ async fn serve_404(response: Response) -> Result<Response, Error> {
 }
 
 #[async_std::main]
-async fn main() -> Result<(), Error> {
+async fn main() -> Result<(), tide::Error> {
     env_logger::init();
+    let matches = app_from_crate!()
+        .arg(
+            Arg::with_name("config")
+                .short("c")
+                .long("config")
+                .value_name("FILE")
+                .takes_value(true)
+                .global(true)
+                .help("Path to configuration file in TOML format"),
+        )
+        .setting(AppSettings::VersionlessSubcommands)
+        .subcommand(SubCommand::with_name("server").about("Serve the website and websocket connections"))
+        .subcommand(
+            SubCommand::with_name("dealer").about("Start a dealer for each game").arg(
+                Arg::with_name("partition")
+                    .value_name("N/M")
+                    .default_value("1/1")
+                    .validator(|partition| partition.parse::<Partition>().map(drop))
+                    .help("Partition all games into M groups and run dealer N of M"),
+            ),
+        )
+        .subcommand(SubCommand::with_name("all").about("Serve the website, websocket connections and start a dealer for each game (default)"))
+        .get_matches();
+    let config = match matches.value_of_os("config") {
+        Some(path) => toml::from_str(&read_to_string(path)?)?,
+        None => Config::default(),
+    };
+
+    let mut run_server = true;
+    let mut run_dealer = true;
+    let mut partition = Partition::default();
+
+    match matches.subcommand() {
+        ("server", _) => run_dealer = false,
+        ("dealer", Some(args)) => {
+            run_server = false;
+            partition = args.value_of("partition").unwrap().parse().unwrap();
+        }
+        _ => {}
+    }
+
+    let signals = Signals::new(&[SIGINT])?;
+    let signal_handler = handle_signals(signals);
 
     const REGISTER_UPDATE_STREAM_CHANNEL_BUFFER: usize = 128;
     let (register_update_stream_tx, register_update_stream_rx) = channel(REGISTER_UPDATE_STREAM_CHANNEL_BUFFER);
 
-    let client = Client::open("redis://localhost/")?;
+    let client = Client::open(config.redis.addr)?;
     let connection = client.get_multiplexed_async_std_connection().await?;
     let pubsub = client.get_async_std_connection().await?.into_pubsub();
-    let mut app = tide::with_state(Server::new(connection, register_update_stream_tx));
-
-    app.at("/").serve_dir("site/")?;
-    app.at("/").serve_file("site/index.html")?;
-    app.at("/api").get(WebSocket::new(new_client));
-    app.with(After(serve_404));
-
-    let signals = Signals::new(&[SIGINT])?;
-    let signal_handler = handle_signals(signals);
-
+    let server = Server::new(connection, register_update_stream_tx);
     let handle_client_interest = handle_client_interest_subscriptions(pubsub, register_update_stream_rx);
-    let handle_new_games = spawn_dealers(app.state().clone());
-
-    /*let listener = TlsListener::build()
-        .addrs("localhost:4433")
-        .cert("cert/cert.pem")
-        .key("cert/key.pem");
-
-    let app = app.listen(listener);*/
-    let app = app.listen("0.0.0.0:8080");
-    pin_mut!(app, handle_client_interest, handle_new_games, signal_handler);
 
-    select(select(app, select(handle_client_interest, handle_new_games).map(|f| f.factor_first().0)).map(|f| f.factor_first().0), signal_handler)
-        .await
-        .factor_first()
-        .0?;
+    let dealer: Pin<Box<dyn Future<Output = Result<(), std::io::Error>>>> =
+        if run_dealer { Box::pin(spawn_dealers(server.clone(), partition)) } else { Box::pin(pending()) };
+
+    let server: Pin<Box<dyn Future<Output = Result<(), std::io::Error>>>> = if run_server {
+        let mut app = tide::with_state(server);
+
+        app.at("/").serve_dir(&config.server.site)?;
+        app.at("/").serve_file(config.server.site.join("index.html"))?;
+        app.at("/api").get(WebSocket::new(new_client));
+        app.with(After(serve_404));
+
+        let mut listener = ConcurrentListener::new();
+        for addrs in config.server.bind {
+            if let (Some(cert), Some(key)) = (&config.server.cert, &config.server.key) {
+                listener.add(TlsListener::build().addrs(addrs).cert(&cert).key(&key))?;
+            } else {
+                listener.add(addrs.to_listener()?)?;
+            }
+        }
+
+        Box::pin(app.listen(listener))
+    } else {
+        Box::pin(pending())
+    };
+
+    select! {
+        _ = signal_handler.fuse() => {},
+        _ = handle_client_interest.fuse() => {},
+        server = server.fuse() => server?,
+        dealer = dealer.fuse() => dealer?,
+    };
 
     info!("Pokerwave shut down gracefully.");
 
index 196d614c5cac917708c0cfc4aee3256a95c501a3..342e31303ff9b02dc826f6513dcde382fb439f7f 100644 (file)
@@ -102,7 +102,7 @@ impl ToRedisArgs for ClientInterest {
     }
 }
 
-pub async fn handle_client_interest_subscriptions(mut connection: PubSub, mut new_clients: Receiver<ClientInterestSender>) -> Result<(), std::io::Error> {
+pub async fn handle_client_interest_subscriptions(mut connection: PubSub, mut new_clients: Receiver<ClientInterestSender>) {
     debug!("handle_client_interest: init");
     let mut clients: Vec<Client> = Vec::new();
     loop {
@@ -212,12 +212,12 @@ pub async fn handle_client_interest_subscriptions(mut connection: PubSub, mut ne
             Action::NoNewClients => {
                 debug!("handle_client_interest: Action::NoNewClients - current clients: {:?}", clients);
                 if clients.is_empty() {
-                    return Ok(());
+                    return;
                 }
             }
             Action::ConnectionClosed => {
                 debug!("handle_client_interest: Action::ConnectionClosed - current clients: {:?}", clients);
-                return Ok(());
+                return;
             }
         }
     }