rust_mc_status/
models.rs

1use std::fmt;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use crate::McError;
5use std::fs::File;
6use std::io::Write;
7use base64::{engine::general_purpose, Engine as _};
8
9#[derive(Debug, Serialize, Deserialize, Clone)]
10pub struct ServerStatus {
11    pub online: bool,
12    pub latency: f64,
13    pub data: ServerData,
14}
15
16impl fmt::Debug for JavaStatus {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        f.debug_struct("JavaStatus")
19            .field("version", &self.version)
20            .field("players", &self.players)
21            .field("description", &self.description)
22            .field("favicon", &self.favicon.as_ref().map(|_| "[Favicon data]"))
23            .field("raw_data", &"[Value]")
24            .finish()
25    }
26}
27
28impl JavaStatus {
29    pub fn save_favicon(&self, filename: &str) -> Result<(), McError> {
30        if let Some(favicon) = &self.favicon {
31            let data = favicon.split(',').nth(1).unwrap_or(favicon);
32            let bytes = general_purpose::STANDARD.decode(data)
33                .map_err(|e| McError::Base64Error(e))?;
34
35            let mut file = File::create(filename)
36                .map_err(|e| McError::IoError(e))?;
37
38            file.write_all(&bytes)
39                .map_err(|e| McError::IoError(e))?;
40
41            Ok(())
42        } else {
43            Err(McError::InvalidResponse("No favicon available".to_string()))
44        }
45    }
46}
47
48#[derive(Debug, Serialize, Deserialize, Clone)]
49#[serde(untagged)]
50pub enum ServerData {
51    Java(JavaStatus),
52    Bedrock(BedrockStatus),
53}
54
55#[derive(Serialize, Deserialize, Clone)]
56pub struct JavaStatus {
57    pub version: JavaVersion,
58    pub players: JavaPlayers,
59    pub description: String,
60    #[serde(skip_serializing)]
61    pub favicon: Option<String>,
62    #[serde(skip)]
63    pub raw_data: Value,
64}
65
66#[derive(Debug, Serialize, Deserialize, Clone)]
67pub struct JavaVersion {
68    pub name: String,
69    pub protocol: i64,
70}
71
72#[derive(Debug, Serialize, Deserialize, Clone)]
73pub struct JavaPlayers {
74    pub online: i64,
75    pub max: i64,
76    pub sample: Option<Vec<JavaPlayer>>,
77}
78
79#[derive(Debug, Serialize, Deserialize, Clone)]
80pub struct JavaPlayer {
81    pub name: String,
82    pub id: String,
83}
84
85#[derive(Serialize, Deserialize, Clone)]
86pub struct BedrockStatus {
87    pub edition: String,
88    pub motd: String,
89    pub protocol_version: String,
90    pub version: String,
91    pub online_players: String,
92    pub max_players: String,
93    pub server_uid: String,
94    pub motd2: String,
95    pub game_mode: String,
96    pub game_mode_numeric: String,
97    pub port_ipv4: String,
98    pub port_ipv6: String,
99    pub raw_data: String,
100}
101
102impl fmt::Debug for BedrockStatus {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        f.debug_struct("BedrockStatus")
105            .field("edition", &self.edition)
106            .field("motd", &self.motd)
107            .field("protocol_version", &self.protocol_version)
108            .field("version", &self.version)
109            .field("online_players", &self.online_players)
110            .field("max_players", &self.max_players)
111            .field("server_uid", &self.server_uid)
112            .field("motd2", &self.motd2)
113            .field("game_mode", &self.game_mode)
114            .field("game_mode_numeric", &self.game_mode_numeric)
115            .field("port_ipv4", &self.port_ipv4)
116            .field("port_ipv6", &self.port_ipv6)
117            .field("raw_data", &"[String]")
118            .finish()
119    }
120}
121
122#[derive(Debug, Serialize, Deserialize, Clone)]
123pub struct ServerInfo {
124    pub address: String,
125    pub edition: ServerEdition,
126}
127
128#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
129pub enum ServerEdition {
130    Java,
131    Bedrock,
132}
133
134impl std::str::FromStr for ServerEdition {
135    type Err = McError;
136
137    fn from_str(s: &str) -> Result<Self, Self::Err> {
138        match s.to_lowercase().as_str() {
139            "java" => Ok(ServerEdition::Java),
140            "bedrock" => Ok(ServerEdition::Bedrock),
141            _ => Err(McError::InvalidEdition(s.to_string())),
142        }
143    }
144}