osrs_highscores/
lib.rs

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}