1use crate::error::Result;
4use crate::nbt::{NbtTag, parse_root_nbt, serialize_root_nbt};
5use bytes::Bytes;
6use serde::{Deserialize, Serialize};
7use std::borrow::Cow;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum PlayerId {
12 Local,
14 Xuid(String),
16 LegacyLevelDat,
18 Unknown(String),
20}
21
22impl PlayerId {
23 #[must_use]
24 pub fn storage_key(&self) -> Option<Cow<'_, [u8]>> {
26 match self {
27 Self::Local => Some(Cow::Borrowed(b"~local_player")),
28 Self::Xuid(xuid) => Some(Cow::Owned(format!("player_{xuid}").into_bytes())),
29 Self::LegacyLevelDat | Self::Unknown(_) => None,
30 }
31 }
32
33 #[must_use]
34 pub fn from_storage_key(key: &[u8]) -> Option<Self> {
36 if key == b"~local_player" {
37 return Some(Self::Local);
38 }
39 let text = std::str::from_utf8(key).ok()?;
40 text.strip_prefix("player_")
41 .map(|xuid| Self::Xuid(xuid.to_string()))
42 }
43}
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct PlayerData {
48 pub id: PlayerId,
50 pub nbt: NbtTag,
52 pub raw: Bytes,
54}
55
56impl PlayerData {
57 pub fn from_raw(id: PlayerId, raw: Bytes) -> Result<Self> {
59 let nbt = parse_root_nbt(&raw)?;
60 Ok(Self { id, nbt, raw })
61 }
62
63 pub fn from_nbt(id: PlayerId, nbt: NbtTag) -> Result<Self> {
65 let raw = Bytes::from(serialize_root_nbt(&nbt)?);
66 Ok(Self { id, nbt, raw })
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn player_keys_roundtrip() {
76 assert_eq!(
77 PlayerId::from_storage_key(b"~local_player"),
78 Some(PlayerId::Local)
79 );
80 assert_eq!(
81 PlayerId::from_storage_key(b"player_123"),
82 Some(PlayerId::Xuid("123".to_string()))
83 );
84 assert_eq!(
85 PlayerId::Xuid("123".to_string()).storage_key().as_deref(),
86 Some(&b"player_123"[..])
87 );
88 }
89}