feat: server side basics

This commit is contained in:
Ahmet Kaan GÜMÜŞ 2024-05-23 04:13:05 +03:00
parent 5111196588
commit 7f31e76881
11 changed files with 266 additions and 440 deletions

3
.gitignore vendored
View file

@ -1 +1,4 @@
/target /target
/.vscode
Cargo.lock

415
Cargo.lock generated
View file

@ -1,415 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "cc"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "keccak"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
dependencies = [
"cpufeatures",
]
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rust-blockchain"
version = "0.1.0"
dependencies = [
"chrono",
"serde",
"serde_json",
"sha3",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sha3"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
dependencies = [
"digest",
"keccak",
]
[[package]]
name = "syn"
version = "2.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

View file

@ -10,3 +10,4 @@ chrono = "0.4.38"
serde = { version = "1.0.202", features = ["derive"] } serde = { version = "1.0.202", features = ["derive"] }
serde_json = "1.0.117" serde_json = "1.0.117"
sha3 = "0.10.8" sha3 = "0.10.8"
tokio = { version = "1.37.0", features = ["full"] }

View file

@ -0,0 +1,3 @@
server_address:127.0.0.1
port:2434
difficulty:1

View file

@ -3,6 +3,7 @@ use std::time::{Duration, Instant};
use chrono::Utc; use chrono::Utc;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha3::{Digest, Sha3_512}; use sha3::{Digest, Sha3_512};
use tokio::sync::broadcast::Sender;
use crate::blockchain::BlockChain; use crate::blockchain::BlockChain;
@ -28,8 +29,14 @@ impl Block {
format!("{:x}", hash) format!("{:x}", hash)
} }
pub fn new(index: u64, data: String, previous_hash: String, instant: Instant) -> Self { pub fn new(
Block { index: u64,
data: String,
previous_hash: String,
instant: Instant,
block_data_channel_sender: Sender<Block>,
) -> Self {
let block = Block {
index, index,
timestamp: Utc::now().timestamp_millis() as u64, timestamp: Utc::now().timestamp_millis() as u64,
data, data,
@ -37,7 +44,9 @@ impl Block {
previous_hash, previous_hash,
hash: String::new(), hash: String::new(),
hash_time_cost: instant.elapsed(), hash_time_cost: instant.elapsed(),
} };
let _ = block_data_channel_sender.send(block.clone());
block
} }
pub fn mine(&mut self, blockhain: BlockChain) -> Self { pub fn mine(&mut self, blockhain: BlockChain) -> Self {

View file

@ -1,10 +1,12 @@
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use chrono::Utc; use chrono::Utc;
use serde::{Deserialize, Serialize};
use tokio::sync::broadcast::Sender;
use crate::block::Block; use crate::block::Block;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockChain { pub struct BlockChain {
pub genesis_block: Block, pub genesis_block: Block,
pub chain: Vec<Block>, pub chain: Vec<Block>,
@ -32,12 +34,18 @@ impl BlockChain {
} }
} }
pub fn add_block(&mut self, data: String, instant: Instant) { pub fn add_block(
&mut self,
data: String,
instant: Instant,
block_data_channel_sender: Sender<Block>,
) {
let new_block = Block::new( let new_block = Block::new(
self.chain.len() as u64, self.chain.len() as u64,
data, data,
self.chain[&self.chain.len() - 1].hash.clone(), self.chain[&self.chain.len() - 1].hash.clone(),
instant, instant,
block_data_channel_sender,
) )
.mine(self.clone()); .mine(self.clone());
self.chain.push(new_block); self.chain.push(new_block);

View file

@ -1,2 +1,17 @@
use std::net::IpAddr;
pub mod block; pub mod block;
pub mod blockchain; pub mod blockchain;
pub mod network;
mod test;
pub mod utils;
pub enum Runner {
Server,
Client,
}
pub struct ServerConfig {
pub server_address: IpAddr,
pub port: u16,
pub difficulty: u8,
}

View file

@ -1,24 +1,36 @@
use std::time::Instant; use rust_blockchain::{
blockchain::BlockChain,
network::start_network,
utils::{read_server_config, take_args},
Runner,
};
use tokio::sync::broadcast;
use rust_blockchain::blockchain::BlockChain; #[tokio::main]
async fn main() {
fn main() {
println!("Hello, world!"); println!("Hello, world!");
let difficulty = 1; match take_args() {
Some(runner) => match runner {
let mut blockchain = BlockChain::new(difficulty); Runner::Server => server().await,
let instant = Instant::now(); Runner::Client => todo!(),
BlockChain::add_block(&mut blockchain, "T".to_string(), Instant::now()); },
BlockChain::add_block(&mut blockchain, "a".to_string(), Instant::now()); None => return,
BlockChain::add_block(&mut blockchain, "h".to_string(), Instant::now()); };
BlockChain::add_block(&mut blockchain, "i".to_string(), Instant::now()); }
BlockChain::add_block(&mut blockchain, "n".to_string(), Instant::now());
BlockChain::add_block(&mut blockchain, "l".to_string(), Instant::now()); async fn server() {
BlockChain::add_block(&mut blockchain, "i".to_string(), Instant::now()); let server_config = match read_server_config() {
println!( Some(server_config) => server_config,
"\t ⛏️⛏️⛏️ | Mined | ⛏️⛏️⛏️\n\n\tElapsed: {:?}\n\n{:#?}", None => return,
instant.elapsed(), };
blockchain
); let blockchain = BlockChain::new(server_config.difficulty.into());
let block_data_channel_sender = broadcast::channel(1).0;
start_network(
server_config,
&blockchain,
block_data_channel_sender.subscribe(),
)
.await;
} }

81
src/network.rs Normal file
View file

@ -0,0 +1,81 @@
use tokio::{
io::AsyncWriteExt,
net::{TcpListener, TcpStream},
sync::broadcast::Receiver,
};
use crate::{block::Block, blockchain::BlockChain, ServerConfig};
pub async fn start_network(
server_config: ServerConfig,
blockchain: &BlockChain,
block_data_channel_receiver: Receiver<Block>,
) {
let listener_socket = match TcpListener::bind(format!(
"{}:{}",
server_config.server_address, server_config.port
))
.await
{
Ok(listener_socket) => listener_socket,
Err(_) => return,
};
loop {
match listener_socket.accept().await {
Ok(connection) => {
tokio::spawn(sync(
connection.0,
blockchain.clone(),
block_data_channel_receiver.resubscribe(),
));
}
Err(_) => {}
}
}
}
async fn sync(
tcp_stream: TcpStream,
blockchain: BlockChain,
block_data_channel_receiver: Receiver<Block>,
) {
let tcp_stream = send_blockchain(tcp_stream, blockchain).await;
send_block(tcp_stream, block_data_channel_receiver).await;
}
async fn send_blockchain(mut tcp_stream: TcpStream, blockchain: BlockChain) -> TcpStream {
let blockchain_data = serde_json::json!({
"blockchain": blockchain
})
.to_string();
match tcp_stream.write_all(&blockchain_data.as_bytes()).await {
Ok(_) => match tcp_stream.flush().await {
Ok(_) => {}
Err(_) => {}
},
Err(_) => {}
}
tcp_stream
}
async fn send_block(mut tcp_stream: TcpStream, mut block_data_channel_receiver: Receiver<Block>) {
loop {
match block_data_channel_receiver.recv().await {
Ok(block) => {
let block_data = serde_json::json!({
"block": block
})
.to_string();
match tcp_stream.write_all(&block_data.as_bytes()).await {
Ok(_) => match tcp_stream.flush().await {
Ok(_) => {}
Err(_) => {}
},
Err(_) => {}
}
}
Err(_) => {}
}
}
}

52
src/test.rs Normal file
View file

@ -0,0 +1,52 @@
#[cfg(test)]
use crate::blockchain::BlockChain;
#[cfg(test)]
use std::time::Duration;
#[cfg(test)]
use std::time::Instant;
#[cfg(test)]
use tokio::sync::broadcast::channel;
#[tokio::test]
async fn create_blockchain() {
let blockchain = BlockChain::new(1);
assert_eq!(blockchain.difficulty, 1);
assert_eq!(blockchain.genesis_block.data, "Tahinli");
assert_eq!(blockchain.genesis_block.hash, "");
assert_eq!(blockchain.genesis_block.index, 0);
assert_eq!(blockchain.genesis_block.previous_hash, "");
assert_eq!(blockchain.genesis_block.proof_of_work, 0);
assert_eq!(
blockchain.genesis_block.hash_time_cost,
Duration::from_secs(0)
);
assert_eq!(blockchain.chain.len(), 1);
assert_eq!(blockchain.chain[0].data, "Tahinli");
assert_eq!(blockchain.chain[0].hash, "");
assert_eq!(blockchain.chain[0].index, 0);
assert_eq!(blockchain.chain[0].previous_hash, "");
assert_eq!(blockchain.chain[0].proof_of_work, 0);
assert_eq!(blockchain.chain[0].hash_time_cost, Duration::from_secs(0));
}
#[tokio::test]
async fn create_block() {
let instant = Instant::now();
let mut blockchain = BlockChain::new(1);
let block_data_channel_sender = channel(1).0;
BlockChain::add_block(
&mut blockchain,
"Ahmet Kaan Gümüş".to_string(),
instant,
block_data_channel_sender.clone(),
);
assert_eq!(blockchain.chain[0].data, "Tahinli");
assert_eq!(blockchain.chain[0].hash, "");
assert_eq!(blockchain.chain[0].index, 0);
assert_eq!(blockchain.chain[0].previous_hash, "");
assert_eq!(blockchain.chain[0].proof_of_work, 0);
assert_eq!(blockchain.chain[0].hash_time_cost, Duration::from_secs(0));
assert_eq!(blockchain.chain[1].data, "Ahmet Kaan Gümüş");
assert_eq!(blockchain.chain[1].previous_hash, "");
assert_eq!(blockchain.chain[1].index, 1);
assert_eq!(blockchain.chain.len(), 2);
}

57
src/utils.rs Normal file
View file

@ -0,0 +1,57 @@
use std::{env, fs::File, io::Read};
use crate::{Runner, ServerConfig};
pub fn take_args() -> Option<Runner> {
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
match &args[1][..] {
"--server" => Some(Runner::Server),
"--client" => Some(Runner::Client),
_ => None,
}
} else {
None
}
}
pub fn read_server_config() -> Option<ServerConfig> {
let mut server_config_file = match File::open("configs/server_config.txt") {
Ok(server_config_file) => server_config_file,
Err(_) => return None,
};
let mut server_configs = String::new();
match server_config_file.read_to_string(&mut server_configs) {
Ok(_) => {
let server_configs: Vec<String> =
server_configs.split("\n").map(|x| x.to_string()).collect();
let server_address = match server_configs[0].split(":").last() {
Some(server_address_unchecked) => match server_address_unchecked.parse() {
Ok(server_address) => server_address,
Err(_) => return None,
},
None => return None,
};
let port = match server_configs[1].split(":").last() {
Some(port_unchecked) => match port_unchecked.parse() {
Ok(port) => port,
Err(_) => return None,
},
None => return None,
};
let difficulty = match server_configs[2].split(":").last() {
Some(difficulty_unchecked) => match difficulty_unchecked.parse() {
Ok(difficulty) => difficulty,
Err(_) => return None,
},
None => return None,
};
Some(ServerConfig {
server_address,
port,
difficulty,
})
}
Err(_) => None,
}
}