1use serde::Deserialize;
8
9use crate::error::{Error, Result};
10
11const PROFILE_URL: &str = "https://api.mojang.com/users/profiles/minecraft";
12const SESSION_URL: &str = "https://sessionserver.mojang.com/session/minecraft/profile";
13
14#[derive(Deserialize)]
15struct MojangProfile {
16 id: String,
17 name: String,
18}
19
20pub async fn uuid_for_username(username: &str) -> Result<Option<String>> {
24 Ok(fetch_profile(&format!("{PROFILE_URL}/{username}"))
25 .await?
26 .map(|p| p.id))
27}
28
29pub async fn username_for_uuid(uuid: &str) -> Result<Option<String>> {
33 let uuid = uuid.replace('-', "");
34 Ok(fetch_profile(&format!("{SESSION_URL}/{uuid}"))
35 .await?
36 .map(|p| p.name))
37}
38
39async fn fetch_profile(url: &str) -> Result<Option<MojangProfile>> {
40 let response = reqwest::get(url).await?;
41 let status = response.status();
42 if status.as_u16() == 404 || status.as_u16() == 204 {
43 return Ok(None);
44 }
45 if !status.is_success() {
46 return Err(Error::Api {
47 status: status.as_u16(),
48 cause: "Mojang API error".to_string(),
49 });
50 }
51 let bytes = response.bytes().await?;
52 Ok(Some(serde_json::from_slice(&bytes)?))
53}