Browse Source

Implement reading configs from toml files.

wip
Titouan Rigoudy 4 years ago
parent
commit
5bc80be02a
3 changed files with 247 additions and 2 deletions
  1. +10
    -0
      Cargo.lock
  2. +1
    -0
      client/Cargo.toml
  3. +236
    -2
      client/src/config.rs

+ 10
- 0
Cargo.lock View File

@ -753,6 +753,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-tungstenite",
"toml",
]
[[package]]
@ -900,6 +901,15 @@ dependencies = [
"tungstenite",
]
[[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.14.0"


+ 1
- 0
client/Cargo.toml View File

@ -19,6 +19,7 @@ solstice-proto = { path = "../proto" }
thiserror = "^1.0"
tokio = { version = "1.0", features = ["full"] }
tokio-tungstenite = "0.15"
toml = "^0.5"
[dev-dependencies]
parking_lot = "^0.11.0"

+ 236
- 2
client/src/config.rs View File

@ -1,7 +1,57 @@
use std::path::Path;
use anyhow::Context;
use serde::{Deserialize, Serialize};
use solstice_proto::server::Credentials;
use tokio::fs::File;
use tokio::io::AsyncReadExt;
/// Represents a TOML configuration file.
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct TomlConfig {
/// The address of the server to connect to.
/// If `None`, a default is used.
pub server_address: Option<String>,
/// User name with which to log in to the server.
user_name: String,
/// Password with which to log in to the server.
password: String,
/// The address on which to listen for incoming peer connections.
/// If `None`, a default value is used.
pub peer_listen_address: Option<String>,
/// The address on which to listen for incoming control connections.
/// If `None`, a default value is used.
pub control_listen_address: Option<String>,
/// The maximum number of peer connections allowed at once.
/// If `None`, a default value is used.
pub max_peers: Option<usize>,
}
impl TomlConfig {
fn from_str(s: &str) -> Result<Self, toml::de::Error> {
toml::from_str(s)
}
async fn from_file_path(path: &Path) -> anyhow::Result<Self> {
let mut file =
File::open(path).await.context("opening toml config file")?;
let mut contents = String::new();
file
.read_to_string(&mut contents)
.await
.context("reading toml config file")?;
// TODO: Implement reading this from .toml files.
#[derive(Debug)]
Self::from_str(&contents).context("parsing toml config file")
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct Config {
/// Credentials to present to the server.
pub credentials: Credentials,
@ -25,11 +75,20 @@ pub struct Config {
pub max_peers: usize,
}
// TODO: Delete in favor of `new`, or mark test-only.
impl Default for Config {
fn default() -> Self {
let credentials =
Credentials::new("solstice".to_string(), "topsekrit".to_string())
.expect("building default credentials");
Self::new(credentials)
}
}
impl Config {
/// Builds a new config with the given `credentials` and default values.
fn new(credentials: Credentials) -> Self {
Self {
credentials,
server_address: "server.slsknet.org:2242".to_string(),
@ -39,4 +98,179 @@ impl Default for Config {
max_peers: 1000,
}
}
/// Attempts to build a `Config` from the given `TomlConfig`.
fn from_toml(toml: TomlConfig) -> anyhow::Result<Self> {
let credentials = Credentials::new(toml.user_name, toml.password)
.context("validating credentials")?;
let mut config = Self::new(credentials);
if let Some(server_address) = toml.server_address {
config.server_address = server_address;
}
if let Some(peer_listen_address) = toml.peer_listen_address {
config.peer_listen_address = peer_listen_address;
}
if let Some(control_listen_address) = toml.control_listen_address {
config.control_listen_address = control_listen_address;
}
if let Some(max_peers) = toml.max_peers {
config.max_peers = max_peers;
}
Ok(config)
}
}
#[cfg(test)]
mod tests {
use std::env;
use std::path::{Path, PathBuf};
use tokio::fs::{DirBuilder, File};
use tokio::io::AsyncWriteExt;
use solstice_proto::server::Credentials;
use super::{Config, TomlConfig};
async fn make_test_dir(test_name: &Path) -> PathBuf {
let path = env::temp_dir()
.join("rust_tests/solstice_client/config")
.join(test_name);
DirBuilder::new()
.recursive(true)
.create(&path)
.await
.expect(&format!("creating test dir {:?}", path));
path
}
#[test]
fn toml_config_from_str_missing_field() {
TomlConfig::from_str(
r#"
user_name = "emiliano"
"#,
)
.unwrap_err();
}
#[test]
fn toml_config_from_str_minimal() {
let config = TomlConfig::from_str(
r#"
user_name = "emiliano"
password = "zapata"
"#,
)
.unwrap();
assert_eq!(
config,
TomlConfig {
user_name: "emiliano".to_string(),
password: "zapata".to_string(),
server_address: None,
peer_listen_address: None,
control_listen_address: None,
max_peers: None,
}
);
}
#[test]
fn toml_config_from_str_maximal() {
let config = TomlConfig::from_str(
r#"
user_name = "emiliano"
password = "zapata"
server_address = "foo.example:1234"
peer_listen_address = "localhost:1337"
control_listen_address = "[::1]:1338"
max_peers = 42
"#,
)
.unwrap();
assert_eq!(
config,
TomlConfig {
user_name: "emiliano".to_string(),
password: "zapata".to_string(),
server_address: Some("foo.example:1234".to_string()),
peer_listen_address: Some("localhost:1337".to_string()),
control_listen_address: Some("[::1]:1338".to_string()),
max_peers: Some(42),
}
);
}
#[tokio::test]
async fn toml_config_from_file_path() {
let dir = make_test_dir(Path::new("toml_config_from_file_path")).await;
let path = dir.join("config.toml");
let mut file = File::create(&path)
.await
.expect(&format!("creating config file {:?}", path));
let contents = r#"
user_name = "emiliano"
password = "zapata"
"#;
file
.write_all(contents.as_bytes())
.await
.expect("writing config file contents");
let config = TomlConfig::from_file_path(&path)
.await
.expect("reading config file");
assert_eq!(
config,
TomlConfig {
user_name: "emiliano".to_string(),
password: "zapata".to_string(),
server_address: None,
peer_listen_address: None,
control_listen_address: None,
max_peers: None,
}
);
}
#[test]
fn config_from_toml_fails_empty_password() {
Config::from_toml(TomlConfig {
user_name: "emiliano".to_string(),
password: "".to_string(),
server_address: None,
peer_listen_address: None,
control_listen_address: None,
max_peers: None,
})
.unwrap_err();
}
#[test]
fn config_from_toml_success() {
let config = Config::from_toml(TomlConfig {
user_name: "emiliano".to_string(),
password: "zapata".to_string(),
server_address: None,
peer_listen_address: None,
control_listen_address: None,
max_peers: None,
})
.unwrap();
let credentials =
Credentials::new("emiliano".to_string(), "zapata".to_string()).unwrap();
assert_eq!(config, Config::new(credentials));
}
}

Loading…
Cancel
Save