veloren_serverbrowser_api/
lib.rs1pub use v1::{Field, FieldContent, GameServer, GameServerList};
2
3pub mod v1 {
4 use country_parser::Country;
5 use serde::{
6 de::{Deserializer, Error, Unexpected},
7 ser::Serializer,
8 Deserialize, Serialize,
9 };
10 use std::collections::HashMap;
11
12 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
13 pub struct GameServerList {
14 pub servers: Vec<GameServer>,
16 }
17
18 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
19 pub struct GameServer {
20 pub name: String,
22 pub address: String,
26 pub port: u16,
28 pub description: String,
30 #[serde(deserialize_with = "deserialize_country")]
34 #[serde(serialize_with = "serialize_country")]
35 #[serde(default)]
36 pub location: Option<Country>,
37 pub auth_server: String,
40 pub query_port: Option<u16>,
45 pub channel: Option<String>,
49 pub official: bool,
52 #[serde(
67 default,
68 skip_serializing_if = "HashMap::is_empty",
69 serialize_with = "ordered_map"
70 )]
71 pub extra: HashMap<String, Field>,
72 }
73
74 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
75 pub struct Field {
76 pub name: String,
80 pub content: FieldContent,
82 }
83
84 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
85 #[non_exhaustive]
86 #[serde(rename_all = "lowercase")]
87 pub enum FieldContent {
88 Text(String),
91 Url(String),
93 #[serde(other)]
96 #[serde(skip_serializing)]
97 Unknown,
98 }
99
100 fn deserialize_country<'de, D: Deserializer<'de>>(de: D) -> Result<Option<Country>, D::Error> {
101 country_parser::parse(String::deserialize(de)?)
102 .map(Some)
103 .ok_or_else(|| {
104 D::Error::invalid_value(
105 Unexpected::Other("invalid country"),
106 &"valid ISO-3166 country",
107 )
108 })
109 }
110
111 fn serialize_country<S: Serializer>(
112 country: &Option<Country>,
113 ser: S,
114 ) -> Result<S::Ok, S::Error> {
115 match country {
116 Some(country) => ser.serialize_str(&country.iso2),
117 None => ser.serialize_none(),
118 }
119 }
120
121 impl GameServer {
122 pub fn new(
123 name: &str,
124 address: &str,
125 port: u16,
126 query_port: Option<u16>,
127 desc: &str,
128 location: Option<Country>,
129 auth: &str,
130 channel: Option<&str>,
131 official: bool,
132 extra: HashMap<String, Field>,
133 ) -> Self {
134 Self {
135 name: name.to_string(),
136 address: address.to_string(),
137 port,
138 query_port,
139 description: desc.to_string(),
140 location,
141 auth_server: auth.to_string(),
142 channel: channel.map(|c| c.to_string()),
143 official,
144 extra,
145 }
146 }
147 }
148
149 fn ordered_map<S>(value: &HashMap<String, Field>, serializer: S) -> Result<S::Ok, S::Error>
150 where
151 S: Serializer,
152 {
153 let ordered: std::collections::BTreeMap<_, _> = value.iter().collect();
154 ordered.serialize(serializer)
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn check_server_list_ron_deserialize() {
164 ron::de::from_reader::<_, GameServerList>(
165 &include_bytes!("../examples/v1/example_server_list.ron")[..],
166 )
167 .unwrap();
168 }
169
170 #[test]
171 fn check_server_list_json_deserialize() {
172 serde_json::de::from_reader::<_, GameServerList>(
173 &include_bytes!("../examples/v1/example_server_list.json")[..],
174 )
175 .unwrap();
176 }
177
178 #[test]
179 fn check_server_list_json_roundtrip() {
180 let data = serde_json::de::from_reader::<_, GameServerList>(
181 &include_bytes!("../examples/v1/example_server_list.json")[..],
182 )
183 .unwrap();
184 serde_json::to_string_pretty(&data).unwrap();
185 }
186
187 #[test]
188 fn serialize_unknown_is_not_possible() {
189 let field = Field {
190 name: "never_serialze".to_string(),
191 content: FieldContent::Unknown,
192 };
193 let result = serde_json::to_string(&field);
194 assert!(result.is_err());
195 assert!(result.unwrap_err().is_data());
196 }
197
198 #[test]
199 fn check_json_schema() {
200 use jsonschema::Validator;
201 use serde_json::Value;
202 let schema = serde_json::de::from_reader::<_, Value>(
203 &include_bytes!("../examples/v1/schema.json")[..],
204 )
205 .unwrap();
206 Validator::new(&schema).expect("A valid schema");
207 }
208
209 #[test]
210 fn validate_json_schema() {
211 use jsonschema::Validator;
212 use serde_json::Value;
213 let schema = serde_json::de::from_reader::<_, Value>(
214 &include_bytes!("../examples/v1/schema.json")[..],
215 )
216 .unwrap();
217 let json = serde_json::de::from_reader::<_, Value>(
218 &include_bytes!("../examples/v1/example_server_list.json")[..],
219 )
220 .unwrap();
221 let compiled = Validator::new(&schema).expect("A valid schema");
222 let result = compiled.validate(&json);
223 if let Err(errors) = result {
224 for error in errors {
225 println!("Validation error: {}", error);
226 println!("Instance path: {}", error.instance_path);
227 }
228 panic!("json schema isn't valid");
229 }
230 }
231}