mc-tcpmux 0.1.0

A TCP port multiplexer for Minecraft servers.
use std::path::Path;

use anyhow::anyhow;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
	pub host: String,
	pub port: u16,
	pub motd: Option<Motd>,

	pub servers: Option<Vec<Server>>,
}

impl Config {
	pub fn load(config_path: &Path) -> anyhow::Result<Self> {
		let config = std::fs::read_to_string(config_path)
			.map_err(|e| anyhow!("Failed to read config file {}: {}", config_path.display(), e))?;
		let config: Config = toml::from_str(&config)
			.map_err(|e| anyhow!("Failed to parse config file {}: {}", config_path.display(), e))?;

		Ok(config)
	}

	pub fn find_server(&self, host: &str, port: u16) -> Option<&Server> {
		self.servers.as_ref().and_then(|l| l.iter().find(|s| s.src.matches(host, port)))
	}
}

#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Server {
	pub src: Target,
	pub dst: String,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Target {
	pub host: Option<String>,
	pub port: Option<u16>,
}

impl Target {
	pub fn matches(&self, host: &str, port: u16) -> bool {
		self.host.as_ref().is_none_or(|p| p == host) && self.port.is_none_or(|p| p == port)
	}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Motd {
	pub version: MotdVersion,
	pub description: Option<serde_json::Value>,
	pub favicon: Option<String>,
	pub players: Option<MotdPlayers>,

	#[serde(default = "default_ping")]
	pub ping: bool,
}

fn default_ping() -> bool {
	true
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub struct MotdVersion {
	pub name: Option<String>,
	pub protocol: Option<i32>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MotdPlayers {
	pub max: Option<i32>,
	pub online: Option<i32>,
	pub sample: Option<Vec<MotdPlayer>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MotdPlayer {
	pub name: String,
	pub id: String,
}