1use serde::{Deserialize, Serialize};
2use ureq::Response;
3
4const STANDARD_HIGH_SCORES: &str = "https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws";
5const IRONMAN_HIGH_SCORES: &str =
6 "https://secure.runescape.com/m=hiscore_oldschool_ironman/index_lite.ws";
7const HARDCORE_HIGH_SCORES: &str =
8 "https://secure.runescape.com/m=hiscore_oldschool_hardcore_ironman/index_lite.ws";
9const ULTIMATE_HIGH_SCORES: &str =
10 "https://secure.runescape.com/m=hiscore_oldschool_hardcore_ultimate/index_lite.ws";
11
12pub fn standard_high_scores(player: &str) -> Result<Player, Box<dyn std::error::Error>> {
13 let url = format!("{}?player={}", STANDARD_HIGH_SCORES, player);
14 make_request_handle_response(&url, player)
15}
16
17pub fn ironman_high_scores(player: &str) -> Result<Player, Box<dyn std::error::Error>> {
18 let url = format!("{}?player={}", IRONMAN_HIGH_SCORES, player);
19 make_request_handle_response(&url, player)
20}
21
22pub fn hardcode_high_scores(player: &str) -> Result<Player, Box<dyn std::error::Error>> {
23 let url = format!("{}?player={}", HARDCORE_HIGH_SCORES, player);
24 make_request_handle_response(&url, player)
25}
26
27pub fn ultimate_high_scores(player: &str) -> Result<Player, Box<dyn std::error::Error>> {
28 let url = format!("{}?player={}", ULTIMATE_HIGH_SCORES, player);
29 make_request_handle_response(&url, player)
30}
31
32fn make_request_handle_response(
33 url: &str,
34 player: &str,
35) -> Result<Player, Box<dyn std::error::Error>> {
36 match ureq::get(&url).call() {
37 Ok(response) => Ok(get_stats_from_response(response, player)?),
38 Err(ureq::Error::Status(404, _)) => Err("Player not found".into()),
39 Err(e) => Err(e.into()),
40 }
41}
42
43fn get_stats_from_response(
44 response: Response,
45 player: &str,
46) -> Result<Player, Box<dyn std::error::Error>> {
47 let body = response.into_string()?;
48 let mut stats = Vec::new();
49
50 let skill_names = [
51 "Overall",
52 "Attack",
53 "Defence",
54 "Strength",
55 "Hitpoints",
56 "Ranged",
57 "Prayer",
58 "Magic",
59 "Cooking",
60 "Woodcutting",
61 "Fletching",
62 "Fishing",
63 "Firemaking",
64 "Crafting",
65 "Smithing",
66 "Mining",
67 "Herblore",
68 "Agility",
69 "Thieving",
70 "Slayer",
71 "Farming",
72 "Runecrafting",
73 "Hunter",
74 "Construction",
75 ];
76
77 for (i, line) in body.lines().enumerate() {
78 if i >= skill_names.len() {
79 break;
80 }
81
82 let tokens: Vec<&str> = line.trim().split(',').collect();
83 if tokens.len() == 3 {
84 let stat = Stat {
85 skill: skill_names[i].to_string(),
86 rank: tokens[0].parse().unwrap_or(-1),
87 level: tokens[1].parse().unwrap_or(-1),
88 xp: tokens[2].parse().unwrap_or(-1),
89 };
90 stats.push(stat);
91 }
92 }
93
94 Ok(Player {
95 name: player.to_string(),
96 stats,
97 })
98}
99
100#[derive(Debug, Serialize, Deserialize)]
101pub struct Player {
102 pub name: String,
103 pub stats: Vec<Stat>,
104}
105
106#[derive(Debug, Serialize, Deserialize)]
107pub struct Stat {
108 pub skill: String,
109 pub rank: i64,
110 pub level: i64,
111 pub xp: i64,
112}