mcsr-ranked-api 0.6.2

MCSR Ranked API wrapper in Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
use chrono::{TimeZone, Utc};
use uuid::uuid;

use crate::{
	game::{
		AdvancedMatchInfo, BastionType, MatchCategory, MatchCompletion, MatchEloUpdate,
		MatchOutcome, MatchRank, MatchSeedInfo, MatchTimelineEvent, MatchType, OverworldType,
	},
	types::Time,
	user::{SupporterTier, UserProfile},
};

#[test]
fn match_info() {
	const JSON: &str = r#"{"id":1524115,"type":2,"seed":{"id":null,"overworld":"VILLAGE","nether":"HOUSING","variations":[]},"category":"ANY","gameMode":"default","players":[{"uuid":"79635c3dbf634a228bf44544cc7c0d27","nickname":"LaysarOwO","roleType":0,"eloRate":1146,"eloRank":1048,"country":null},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","nickname":"Frigbob","roleType":0,"eloRate":1470,"eloRank":211,"country":null}],"spectators":[],"result":{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":620128},"forfeited":false,"decayed":false,"rank":{"season":1000,"allTime":null},"changes":[{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","change":19,"eloRate":1282},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","change":-19,"eloRate":1245}],"completions":[{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":620128}],"timelines":[{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":610048,"type":"projectelo.timeline.dragon_death"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":535511,"type":"end.root"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":535393,"type":"story.enter_the_end"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":494792,"type":"story.follow_ender_eye"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":454844,"type":"story.follow_ender_eye"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":419050,"type":"projectelo.timeline.blind_travel"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":358761,"type":"projectelo.timeline.blind_travel"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":354196,"type":"nether.obtain_blaze_rod"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":331692,"type":"nether.obtain_blaze_rod"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":320335,"type":"nether.find_fortress"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":311082,"type":"adventure.root"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":310966,"type":"adventure.kill_a_mob"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":310597,"type":"nether.find_fortress"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":290740,"type":"husbandry.root"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":282496,"type":"adventure.kill_a_mob"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":282404,"type":"adventure.root"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":266018,"type":"nether.loot_bastion"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":255043,"type":"story.form_obsidian"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":235241,"type":"nether.obtain_crying_obsidian"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":230341,"type":"nether.obtain_crying_obsidian"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":223692,"type":"story.form_obsidian"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":170186,"type":"nether.distract_piglin"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":162694,"type":"nether.loot_bastion"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":161055,"type":"nether.find_bastion"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":157338,"type":"nether.find_bastion"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":153691,"type":"husbandry.root"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":132943,"type":"story.enter_the_nether"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":132837,"type":"nether.root"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":120786,"type":"story.lava_bucket"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":118642,"type":"nether.root"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":118522,"type":"story.enter_the_nether"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":106937,"type":"story.lava_bucket"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":37230,"type":"story.root"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":33038,"type":"story.mine_stone"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":31439,"type":"story.root"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":29794,"type":"story.obtain_armor"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":29794,"type":"story.iron_tools"},{"uuid":"79635c3dbf634a228bf44544cc7c0d27","time":29684,"type":"story.smelt_iron"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":24938,"type":"story.mine_stone"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":23237,"type":"story.iron_tools"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":22337,"type":"story.obtain_armor"},{"uuid":"7d3a6bb9f62645ae80cf40840ca84c50","time":21037,"type":"story.smelt_iron"}],"season":7,"date":1735326765,"seedType":"VILLAGE","bastionType":"HOUSING","tag":null,"replayExist":false}"#;

	let full_info: AdvancedMatchInfo = serde_json::from_str(JSON).unwrap();
	let info = &full_info.info;
	assert_eq!(info.id, 1524115);
	assert_eq!(info.kind, MatchType::Ranked);
	assert_eq!(info.season, 7);
	assert_eq!(info.category, Some(MatchCategory::Any));
	assert_eq!(info.date, Utc.timestamp_opt(1735326765, 0).unwrap());
	let mut players = info.players().to_vec();
	players.sort_by_key(|l| l.uuid);
	assert_eq!(
		players,
		[
			UserProfile::new(
				"79635c3dbf634a228bf44544cc7c0d27",
				"LaysarOwO",
				SupporterTier::None,
				Some(1146),
				Some(1048),
				None,
			),
			UserProfile::new(
				"7d3a6bb9f62645ae80cf40840ca84c50",
				"Frigbob",
				SupporterTier::None,
				Some(1470),
				Some(211),
				None,
			),
		]
	);
	let frigidbob = uuid!("7d3a6bb9f62645ae80cf40840ca84c50");
	let laysar = uuid!("79635c3dbf634a228bf44544cc7c0d27");
	assert_eq!(info.spectators(), []);
	assert_eq!(
		info.seed,
		Some(MatchSeedInfo {
			id: None,
			overworld: Some(OverworldType::Village),
			bastion: Some(BastionType::Housing),
			variations: [].into(),
			end_towers: None,
		})
	);
	assert_eq!(info.overworld_type(), Some(OverworldType::Village));
	assert_eq!(info.bastion_type(), Some(BastionType::Housing));
	assert_eq!(
		info.result,
		MatchOutcome {
			winner_uuid: Some(frigidbob),
			time: Time(620128),
		}
	);
	assert!(!info.forfeited);
	assert!(!info.decayed);
	assert_eq!(
		info.rank,
		MatchRank {
			season: Some(1000),
			all_time: None,
		}
	);
	assert_eq!(
		info.elo_updates(),
		[
			MatchEloUpdate {
				player_uuid: frigidbob,
				change: Some(19),
				elo: Some(1282),
			},
			MatchEloUpdate {
				player_uuid: laysar,
				change: Some(-19),
				elo: Some(1245),
			}
		],
	);
	assert_eq!(
		full_info.completions(),
		[MatchCompletion {
			player_uuid: frigidbob,
			time: Time(620128),
		}]
	);
	assert_eq!(
		full_info.timeline_events(),
		[
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(610048),
				id: "projectelo.timeline.dragon_death".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(535511),
				id: "end.root".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(535393),
				id: "story.enter_the_end".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(494792),
				id: "story.follow_ender_eye".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(454844),
				id: "story.follow_ender_eye".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(419050),
				id: "projectelo.timeline.blind_travel".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(358761),
				id: "projectelo.timeline.blind_travel".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(354196),
				id: "nether.obtain_blaze_rod".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(331692),
				id: "nether.obtain_blaze_rod".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(320335),
				id: "nether.find_fortress".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(311082),
				id: "adventure.root".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(310966),
				id: "adventure.kill_a_mob".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(310597),
				id: "nether.find_fortress".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(290740),
				id: "husbandry.root".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(282496),
				id: "adventure.kill_a_mob".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(282404),
				id: "adventure.root".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(266018),
				id: "nether.loot_bastion".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(255043),
				id: "story.form_obsidian".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(235241),
				id: "nether.obtain_crying_obsidian".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(230341),
				id: "nether.obtain_crying_obsidian".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(223692),
				id: "story.form_obsidian".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(170186),
				id: "nether.distract_piglin".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(162694),
				id: "nether.loot_bastion".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(161055),
				id: "nether.find_bastion".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(157338),
				id: "nether.find_bastion".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(153691),
				id: "husbandry.root".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(132943),
				id: "story.enter_the_nether".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(132837),
				id: "nether.root".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(120786),
				id: "story.lava_bucket".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(118642),
				id: "nether.root".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(118522),
				id: "story.enter_the_nether".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(106937),
				id: "story.lava_bucket".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(37230),
				id: "story.root".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(33038),
				id: "story.mine_stone".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(31439),
				id: "story.root".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(29794),
				id: "story.obtain_armor".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(29794),
				id: "story.iron_tools".into(),
			},
			MatchTimelineEvent {
				player_uuid: laysar,
				time: Time(29684),
				id: "story.smelt_iron".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(24938),
				id: "story.mine_stone".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(23237),
				id: "story.iron_tools".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(22337),
				id: "story.obtain_armor".into(),
			},
			MatchTimelineEvent {
				player_uuid: frigidbob,
				time: Time(21037),
				id: "story.smelt_iron".into(),
			}
		]
	);
	assert!(!full_info.replay_exists);
	#[cfg(feature = "serialize")]
	{
		let re_deserialized: AdvancedMatchInfo =
			serde_json::from_str(&serde_json::to_string(&full_info).unwrap()).unwrap();
		assert_eq!(full_info, re_deserialized)
	}
}

#[test]
fn match_category() {
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ANY""#).unwrap(),
		MatchCategory::Any
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""CUSTOM""#).unwrap(),
		MatchCategory::Custom
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""HIGH""#).unwrap(),
		MatchCategory::High
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""KILL_ALL_BOSSES""#).unwrap(),
		MatchCategory::KillAllBosses
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""KILL_WITHER""#).unwrap(),
		MatchCategory::KillWither
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""KILL_ELDER_GUARDIAN""#).unwrap(),
		MatchCategory::KillElderGuardian
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ALL_ADVANCEMENTS""#).unwrap(),
		MatchCategory::AllAdvancements
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""HALF""#).unwrap(),
		MatchCategory::Half
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""POGLOOT_QUATER""#).unwrap(),
		MatchCategory::PoglootQuater
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""HOW_DID_WE_GET_HERE""#).unwrap(),
		MatchCategory::HowDidWeGetHere
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""HERO_OF_THE_VILLAGE""#).unwrap(),
		MatchCategory::HeroOfTheVillage
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ARBALISTIC""#).unwrap(),
		MatchCategory::Arbalistic
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""COVER_ME_IN_DEBRIS""#).unwrap(),
		MatchCategory::CoverMeInDebris
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ENTER_NETHER""#).unwrap(),
		MatchCategory::EnterNether
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ENTER_END""#).unwrap(),
		MatchCategory::EnterEnd
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ALL_SWORDS""#).unwrap(),
		MatchCategory::AllSwords
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ALL_MINERALS""#).unwrap(),
		MatchCategory::AllMinerals
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""FULL_IA_15_LVL""#).unwrap(),
		MatchCategory::FullIa15Lvl
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ALL_WORKSTATIONS""#).unwrap(),
		MatchCategory::AllWorkstations
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""FULL_INV""#).unwrap(),
		MatchCategory::FullInv
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""STACK_OF_LIME_WOOL""#).unwrap(),
		MatchCategory::StackOfLimeWool
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ALL_PORTALS""#).unwrap(),
		MatchCategory::AllPortals
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""ALL_BLOCKS""#).unwrap(),
		MatchCategory::AllBlocks
	);
	assert_eq!(
		serde_json::from_str::<MatchCategory>(r#""MINE_A_CHUNK""#).unwrap(),
		MatchCategory::MineAChunk
	);
}

#[test]
fn match_type() {
	assert_eq!(
		serde_json::from_str::<MatchType>("1").unwrap(),
		MatchType::Causal
	);
	assert_eq!(
		serde_json::from_str::<MatchType>("2").unwrap(),
		MatchType::Ranked
	);
	assert_eq!(
		serde_json::from_str::<MatchType>("3").unwrap(),
		MatchType::Private
	);
	assert_eq!(
		serde_json::from_str::<MatchType>("4").unwrap(),
		MatchType::Event
	);
	assert!(serde_json::from_str::<MatchType>("0").is_err());
	assert!(serde_json::from_str::<MatchType>("99").is_err());
}

#[test]
fn overworld_type() {
	assert_eq!(
		serde_json::from_str::<OverworldType>(r#""VILLAGE""#).unwrap(),
		OverworldType::Village
	);
	assert_eq!(
		serde_json::from_str::<OverworldType>(r#""BURIED_TREASURE""#).unwrap(),
		OverworldType::BuriedTreasure
	);
	assert_eq!(
		serde_json::from_str::<OverworldType>(r#""SHIPWRECK""#).unwrap(),
		OverworldType::Shipwreck
	);
	assert_eq!(
		serde_json::from_str::<OverworldType>(r#""DESERT_TEMPLE""#).unwrap(),
		OverworldType::DesertTemple
	);
	assert_eq!(
		serde_json::from_str::<OverworldType>(r#""RUINED_PORTAL""#).unwrap(),
		OverworldType::RuinedPortal
	);
	assert!(serde_json::from_str::<OverworldType>(r#""DOESN'T EXIST""#).is_err());
}

#[test]
fn bastion_type() {
	assert_eq!(
		serde_json::from_str::<BastionType>(r#""HOUSING""#).unwrap(),
		BastionType::Housing
	);
	assert_eq!(
		serde_json::from_str::<BastionType>(r#""TREASURE""#).unwrap(),
		BastionType::Treasure
	);
	assert_eq!(
		serde_json::from_str::<BastionType>(r#""BRIDGE""#).unwrap(),
		BastionType::Bridge
	);
	assert_eq!(
		serde_json::from_str::<BastionType>(r#""STABLES""#).unwrap(),
		BastionType::Stables
	);
	assert!(serde_json::from_str::<BastionType>(r#""DOESN'T EXIST""#).is_err());

	#[cfg(feature = "serialize")]
	{
		assert_eq!(
			serde_json::to_string(&BastionType::Housing).unwrap(),
			r#""HOUSING""#
		);
		assert_eq!(
			serde_json::to_string(&BastionType::Treasure).unwrap(),
			r#""TREASURE""#
		);
		assert_eq!(
			serde_json::to_string(&BastionType::Bridge).unwrap(),
			r#""BRIDGE""#
		);
		assert_eq!(
			serde_json::to_string(&BastionType::Stables).unwrap(),
			r#""STABLES""#
		);
	}
}