1use serde::{Deserialize, Serialize};
2
3#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
4#[non_exhaustive]
5#[serde(rename_all = "camelCase")]
6pub struct JavaServerInfo {
7 pub version: Option<ServerVersion>,
8 pub players: Option<ServerPlayers>,
9 pub description: TextComponent,
10 pub favicon: Option<String>,
11 #[serde(rename = "modinfo")]
12 pub mod_info: Option<ServerModInfo>,
13 pub prevents_chat_reports: Option<bool>,
16 pub previews_chat: Option<bool>,
18 pub enforces_secure_chat: Option<bool>,
20}
21
22#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
23#[non_exhaustive]
24pub struct ServerVersion {
25 pub name: String,
26 pub protocol: u32,
27}
28
29#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
30#[non_exhaustive]
31pub struct ServerPlayers {
32 pub max: u32,
33 pub online: u32,
34 pub sample: Option<Vec<ServerPlayersSample>>,
35}
36
37#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
39#[non_exhaustive]
40pub struct ServerPlayersSample {
41 pub name: Option<String>,
43 pub id: Option<String>,
45}
46
47impl ServerPlayersSample {
48 pub fn is_anonymous(&self) -> bool {
50 self.id
51 .as_deref()
52 .map_or(true, |id| id == "00000000-0000-0000-0000-000000000000")
53 }
54}
55
56#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
57#[non_exhaustive]
58pub struct ServerModInfo {
59 #[serde(rename = "type")]
60 pub loader_type: String,
61 #[serde(rename = "modList")]
62 pub mod_list: Vec<ServerMod>,
63}
64
65#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
66#[non_exhaustive]
67pub struct ServerMod {
68 #[serde(rename = "modid")]
69 pub mod_id: String,
70 pub version: String,
71}
72
73impl std::str::FromStr for JavaServerInfo {
74 type Err = serde_json::Error;
75 fn from_str(json: &str) -> Result<Self, Self::Err> {
76 serde_json::from_str(json)
77 }
78}
79
80#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
81#[serde(untagged)]
82pub enum TextComponent {
83 Plain(String),
84 Fancy(FancyText),
85 Extra(Vec<TextComponent>),
86}
87
88#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, Default)]
89#[non_exhaustive]
90pub struct FancyText {
91 #[serde(default)]
92 pub text: Option<String>,
93 #[serde(default)]
94 pub color: Option<String>,
95 #[serde(default)]
96 pub bold: Option<bool>,
97 #[serde(default)]
98 pub italic: Option<bool>,
99 #[serde(default)]
100 pub underlined: Option<bool>,
101 #[serde(default)]
102 pub strikethrough: Option<bool>,
103 #[serde(default)]
104 pub obfuscated: Option<bool>,
105 #[serde(default)]
106 pub extra: Option<Vec<TextComponent>>,
107}
108
109impl From<TextComponent> for FancyText {
110 fn from(value: TextComponent) -> Self {
111 match value {
112 TextComponent::Plain(text) => FancyText {
113 text: Some(text),
114 ..Default::default()
115 },
116 TextComponent::Fancy(fancy) => fancy,
117 TextComponent::Extra(components) => {
118 let mut components = components.into_iter();
119 let mut first = components.next().map(FancyText::from).unwrap_or_default();
120 first.extra.get_or_insert_with(Vec::new).extend(components);
121 first
122 }
123 }
124 }
125}