1use indexmap::IndexMap;
2
3use crate::error::{Error, Result};
4
5pub type CompoundTag = IndexMap<String, Tag>;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[repr(u8)]
9pub enum TagType {
10 End = 0,
11 Byte = 1,
12 Short = 2,
13 Int = 3,
14 Long = 4,
15 Float = 5,
16 Double = 6,
17 ByteArray = 7,
18 String = 8,
19 List = 9,
20 Compound = 10,
21 IntArray = 11,
22 LongArray = 12,
23}
24
25impl TagType {
26 pub const fn id(self) -> u8 {
27 self as u8
28 }
29}
30
31impl TryFrom<u8> for TagType {
32 type Error = Error;
33
34 fn try_from(value: u8) -> Result<Self> {
35 let tag_type = match value {
36 0 => Self::End,
37 1 => Self::Byte,
38 2 => Self::Short,
39 3 => Self::Int,
40 4 => Self::Long,
41 5 => Self::Float,
42 6 => Self::Double,
43 7 => Self::ByteArray,
44 8 => Self::String,
45 9 => Self::List,
46 10 => Self::Compound,
47 11 => Self::IntArray,
48 12 => Self::LongArray,
49 _ => return Err(Error::UnknownTag { id: value }),
50 };
51 Ok(tag_type)
52 }
53}
54
55#[derive(Debug, Clone, PartialEq)]
56pub struct ListTag {
57 pub element_type: TagType,
58 pub elements: Vec<Tag>,
59}
60
61impl ListTag {
62 pub fn new(element_type: TagType, elements: Vec<Tag>) -> Result<Self> {
63 let list = Self {
64 element_type,
65 elements,
66 };
67 list.validate()?;
68 Ok(list)
69 }
70
71 pub fn empty(element_type: TagType) -> Self {
72 Self {
73 element_type,
74 elements: Vec::new(),
75 }
76 }
77
78 pub fn validate(&self) -> Result<()> {
79 if self.element_type == TagType::End && !self.elements.is_empty() {
80 return Err(Error::InvalidListHeader {
81 element_type_id: self.element_type.id(),
82 length: self.elements.len(),
83 });
84 }
85
86 for element in &self.elements {
87 let actual = element.tag_type();
88 if actual != self.element_type {
89 return Err(Error::UnexpectedType {
90 context: "list_element_type",
91 expected_id: self.element_type.id(),
92 actual_id: actual.id(),
93 });
94 }
95 }
96 Ok(())
97 }
98}
99
100#[derive(Debug, Clone, PartialEq)]
101pub enum Tag {
102 End,
107 Byte(i8),
108 Short(i16),
109 Int(i32),
110 Long(i64),
111 Float(f32),
112 Double(f64),
113 ByteArray(Vec<u8>),
114 String(String),
115 List(ListTag),
116 Compound(CompoundTag),
117 IntArray(Vec<i32>),
118 LongArray(Vec<i64>),
119}
120
121impl Tag {
122 pub fn tag_type(&self) -> TagType {
123 match self {
124 Tag::End => TagType::End,
125 Tag::Byte(_) => TagType::Byte,
126 Tag::Short(_) => TagType::Short,
127 Tag::Int(_) => TagType::Int,
128 Tag::Long(_) => TagType::Long,
129 Tag::Float(_) => TagType::Float,
130 Tag::Double(_) => TagType::Double,
131 Tag::ByteArray(_) => TagType::ByteArray,
132 Tag::String(_) => TagType::String,
133 Tag::List(_) => TagType::List,
134 Tag::Compound(_) => TagType::Compound,
135 Tag::IntArray(_) => TagType::IntArray,
136 Tag::LongArray(_) => TagType::LongArray,
137 }
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn end_tag_maps_to_end_tag_type() {
147 assert_eq!(Tag::End.tag_type(), TagType::End);
148 }
149}