valence_protocol/
profile.rs

1use base64::prelude::*;
2use serde::{Deserialize, Serialize};
3use url::Url;
4
5use crate::{Decode, Encode};
6
7/// A property from the game profile.
8#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Encode, Decode)]
9pub struct Property<S = String> {
10    pub name: S,
11    pub value: S,
12    pub signature: Option<S>,
13}
14
15/// Contains URLs to the skin and cape of a player.
16#[derive(Clone, PartialEq, Eq, Debug)]
17pub struct PlayerTextures {
18    /// URL to the player's skin texture.
19    pub skin: Url,
20    /// URL to the player's cape texture. May be absent if the player does not
21    /// have a cape.
22    pub cape: Option<Url>,
23}
24
25impl PlayerTextures {
26    /// Constructs player textures from the "textures" property of the game
27    /// profile.
28    ///
29    /// "textures" is a base64 string of JSON data.
30    pub fn try_from_textures(textures: &str) -> anyhow::Result<Self> {
31        #[derive(Debug, Deserialize)]
32        struct Textures {
33            textures: PlayerTexturesPayload,
34        }
35
36        #[derive(Debug, Deserialize)]
37        #[serde(rename_all = "UPPERCASE")]
38        struct PlayerTexturesPayload {
39            skin: TextureUrl,
40            #[serde(default)]
41            cape: Option<TextureUrl>,
42        }
43
44        #[derive(Debug, Deserialize)]
45        struct TextureUrl {
46            url: Url,
47        }
48
49        let decoded = BASE64_STANDARD.decode(textures.as_bytes())?;
50
51        let Textures { textures } = serde_json::from_slice(&decoded)?;
52
53        Ok(Self {
54            skin: textures.skin.url,
55            cape: textures.cape.map(|t| t.url),
56        })
57    }
58}