use crate::byte_order::ByteOrder;
use crate::errors::Error;
#[cfg(feature = "serde")]
use serde::ser::SerializeMap;
const INDENT: &'static str = " ";
pub type Name = Option<String>;
#[derive(Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum Tag {
Byte(Name, i8) = 1,
Short(Name, i16) = 2,
Int(Name, i32) = 3,
Long(Name, i64) = 4,
Float(Name, f32) = 5,
Double(Name, f64) = 6,
ByteArray(Name, Vec<i8>) = 7,
String(Name, String) = 8,
List(Name, Vec<Tag>) = 9,
Compound(Name, Vec<Tag>) = 10,
IntArray(Name, Vec<i32>) = 11,
LongArray(Name, Vec<i64>) = 12,
}
#[cfg(feature = "serde")]
impl serde::Serialize for Tag {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(Some(2))?;
match self {
Tag::Byte(name, payload) => {
map.serialize_entry("type", "byte")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::Short(name, payload) => {
map.serialize_entry("type", "short")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::Int(name, payload) => {
map.serialize_entry("type", "int")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::Long(name, payload) => {
map.serialize_entry("type", "long")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::Float(name, payload) => {
map.serialize_entry("type", "float")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::Double(name, payload) => {
map.serialize_entry("type", "double")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::ByteArray(name, payload) => {
map.serialize_entry("type", "byte_array")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::String(name, payload) => {
map.serialize_entry("type", "string")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::List(name, payload) => {
map.serialize_entry("type", "list")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::Compound(name, payload) => {
map.serialize_entry("type", "compound")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::IntArray(name, payload) => {
map.serialize_entry("type", "int_array")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
Tag::LongArray(name, payload) => {
map.serialize_entry("type", "long_array")?;
map.serialize_entry("name", name)?;
map.serialize_entry("payload", payload)?;
}
};
map.end()
}
}
impl Tag {
pub fn to_bytes(&self, byte_order: ByteOrder) -> Result<Vec<u8>, Error> {
let mut buf = vec![];
buf.extend(self.bytes_id(byte_order));
buf.extend(self.bytes_name(byte_order)?);
buf.extend(self.bytes_payload(byte_order)?);
Ok(buf)
}
#[cfg(feature = "read")]
pub fn from_bytes(bytes: &[u8], byte_order: ByteOrder) -> Result<Self, Error> {
Ok(crate::parser::nbt(bytes, byte_order)
.map_err(|e| match e {
nom::Err::Incomplete(needed) => Error::Incomplete(needed),
nom::Err::Error(e) | nom::Err::Failure(e) => Error::ParseError(e.code),
})?
.1)
}
pub fn name(&self) -> &'static str {
match self {
Tag::Byte(_, _) => "TAG_Byte",
Tag::Short(_, _) => "TAG_Short",
Tag::Int(_, _) => "TAG_Int",
Tag::Long(_, _) => "TAG_Long",
Tag::Float(_, _) => "TAG_Float",
Tag::Double(_, _) => "TAG_Double",
Tag::ByteArray(_, _) => "TAG_Byte_Array",
Tag::String(_, _) => "TAG_String",
Tag::List(_, _) => "TAG_List",
Tag::Compound(_, _) => "TAG_Compound",
Tag::IntArray(_, _) => "TAG_Int_Array",
Tag::LongArray(_, _) => "TAG_Long_Array",
}
}
pub fn pretty(&self) -> String {
self.pretty_truncated(0)
}
pub fn pretty_truncated(&self, truncate: u64) -> String {
let mut result = String::new();
match self {
Tag::Byte(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {}", payload));
}
Tag::Short(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {}", payload));
}
Tag::Int(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {}", payload));
}
Tag::Long(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {}", payload));
}
Tag::Float(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {}", payload));
}
Tag::Double(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {}", payload));
}
Tag::ByteArray(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {} entries\n{{\n", payload.len()));
let mut bytes = payload.into_iter();
for i in 1.. {
match bytes.next() {
Some(value) => {
result.push_str(INDENT);
result.push_str(&value.to_string());
result.push('\n');
}
None => break,
}
if i == truncate {
break;
}
}
let remaining = bytes.len();
if remaining != 0 {
result.push_str(&format!("{}[and {} more]\n", INDENT, remaining));
}
result.push('}');
}
Tag::String(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": '{}'", payload));
}
Tag::List(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {} entries\n{{\n", payload.len()));
let mut values = payload.iter();
for i in 1.. {
match values.next() {
Some(value) => {
for line in value.pretty_truncated(truncate).lines() {
result.push_str(INDENT);
result.push_str(&line);
result.push('\n');
}
}
None => break,
}
if i == truncate {
break;
}
}
let remaining = values.len();
if remaining != 0 {
result.push_str(&format!("{}[and {} more]\n", INDENT, remaining));
}
result.push('}');
}
Tag::Compound(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {} entries\n{{\n", payload.len()));
for tag in payload {
for line in tag.pretty_truncated(truncate).lines() {
result.push_str(INDENT);
result.push_str(&line);
result.push('\n');
}
}
result.push('}');
}
Tag::IntArray(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {} entries\n{{\n", payload.len()));
let mut ints = payload.into_iter();
for i in 1.. {
match ints.next() {
Some(value) => {
result.push_str(INDENT);
result.push_str(&value.to_string());
result.push('\n');
}
None => break,
}
if i == truncate {
break;
}
}
let remaining = ints.len();
if remaining != 0 {
result.push_str(&format!("{}[and {} more]\n", INDENT, remaining));
}
result.push('}');
}
Tag::LongArray(name, payload) => {
result.push_str(self.name());
if let Some(value) = name {
result.push_str(&format!("(\"{}\")", value));
};
result.push_str(&format!(": {} entries\n{{\n", payload.len()));
let mut longs = payload.into_iter();
for i in 1.. {
match longs.next() {
Some(value) => {
result.push_str(INDENT);
result.push_str(&value.to_string());
result.push('\n');
}
None => break,
}
if i == truncate {
break;
}
}
let remaining = longs.len();
if remaining != 0 {
result.push_str(&format!("{}[and {} more]\n", INDENT, remaining));
}
result.push('}');
}
}
result
}
fn bytes_name(&self, byte_order: ByteOrder) -> Result<Vec<u8>, Error> {
let mut buf = vec![];
match self {
Tag::Byte(name, _)
| Tag::Short(name, _)
| Tag::Int(name, _)
| Tag::Long(name, _)
| Tag::Float(name, _)
| Tag::Double(name, _)
| Tag::ByteArray(name, _)
| Tag::String(name, _)
| Tag::List(name, _)
| Tag::Compound(name, _)
| Tag::IntArray(name, _)
| Tag::LongArray(name, _) => {
let n = name.as_ref().ok_or(Error::MissingName)?;
let n = mutf8::encode(n).into_owned();
let len: u16 = n.len().try_into().map_err(|_| Error::StringTooBig)?;
buf.extend(byte_order.bytes(len));
buf.extend(n);
}
};
Ok(buf)
}
fn bytes_id(&self, byte_order: ByteOrder) -> Vec<u8> {
let mut buf = vec![];
buf.extend(match *self {
Tag::Byte(..) => byte_order.bytes(1_i8),
Tag::Short(..) => byte_order.bytes(2_i8),
Tag::Int(..) => byte_order.bytes(3_i8),
Tag::Long(..) => byte_order.bytes(4_i8),
Tag::Float(..) => byte_order.bytes(5_i8),
Tag::Double(..) => byte_order.bytes(6_i8),
Tag::ByteArray(..) => byte_order.bytes(7_i8),
Tag::String(..) => byte_order.bytes(8_i8),
Tag::List(..) => byte_order.bytes(9_i8),
Tag::Compound(..) => byte_order.bytes(10_i8),
Tag::IntArray(..) => byte_order.bytes(11_i8),
Tag::LongArray(..) => byte_order.bytes(12_i8),
});
buf
}
fn bytes_payload(&self, byte_order: ByteOrder) -> Result<Vec<u8>, Error> {
let mut buf = vec![];
match self {
Tag::Byte(_, payload) => buf.extend(byte_order.bytes(*payload)),
Tag::Short(_, payload) => buf.extend(byte_order.bytes(*payload)),
Tag::Int(_, payload) => buf.extend(byte_order.bytes(*payload)),
Tag::Long(_, payload) => buf.extend(byte_order.bytes(*payload)),
Tag::Float(_, payload) => buf.extend(byte_order.bytes(*payload)),
Tag::Double(_, payload) => buf.extend(byte_order.bytes(*payload)),
Tag::ByteArray(_, payload) => {
let len: i32 = payload
.len()
.try_into()
.map_err(|_| Error::ByteArrayTooBig)?;
buf.extend(byte_order.bytes(len));
for byte in payload {
buf.extend(byte_order.bytes(*byte));
}
}
Tag::String(_, payload) => {
let string = mutf8::encode(payload.as_str());
let len: u16 = string.len().try_into().map_err(|_| Error::StringTooBig)?;
buf.extend(byte_order.bytes(len));
buf.extend(string.into_owned());
}
Tag::List(_, payload) => {
match payload.first() {
Some(first) => buf.extend(first.bytes_id(byte_order)),
None => buf.extend(byte_order.bytes(0_i8)),
}
let len: i32 = payload.len().try_into().map_err(|_| Error::ListTooBig)?;
buf.extend(byte_order.bytes(len));
for byte in payload {
buf.extend(byte.bytes_payload(byte_order)?);
}
}
Tag::Compound(_, payload) => {
for tag in payload {
buf.extend(tag.to_bytes(byte_order)?);
}
buf.extend(byte_order.bytes(0_i8));
}
Tag::IntArray(_, payload) => {
let len: i32 = payload
.len()
.try_into()
.map_err(|_| Error::IntArrayTooBig)?;
buf.extend(byte_order.bytes(len));
for int in payload {
buf.extend(byte_order.bytes(*int));
}
}
Tag::LongArray(_, payload) => {
let len: i32 = payload
.len()
.try_into()
.map_err(|_| Error::LongArrayTooBig)?;
buf.extend(byte_order.bytes(len));
for long in payload {
buf.extend(byte_order.bytes(*long));
}
}
};
Ok(buf)
}
}
#[macro_export]
macro_rules! nbt {
($($data:expr),* $(,)?) => {
Tag::Compound(Some(String::new()), vec![$($data),*])
};
}
#[macro_export]
macro_rules! byte {
($name:expr => $value:expr $(,)?) => {
Tag::Byte(Some(String::from($name)), $value)
};
($value:expr $(,)?) => {
Tag::Byte(None, $value)
};
}
#[macro_export]
macro_rules! short {
($name:expr => $value:expr $(,)?) => {
Tag::Short(Some(String::from($name)), $value)
};
($value:expr $(,)?) => {
Tag::Short(None, $value)
};
}
#[macro_export]
macro_rules! int {
($name:expr => $value:expr $(,)?) => {
Tag::Int(Some(String::from($name)), $value)
};
($value:expr $(,)?) => {
Tag::Int(None, $value)
};
}
#[macro_export]
macro_rules! long {
($name:expr => $value:expr $(,)?) => {
Tag::Long(Some(String::from($name)), $value)
};
($value:expr $(,)?) => {
Tag::Long(None, $value)
};
}
#[macro_export]
macro_rules! float {
($name:expr => $value:expr $(,)?) => {
Tag::Float(Some(String::from($name)), $value)
};
($value:expr $(,)?) => {
Tag::Float(None, $value)
};
}
#[macro_export]
macro_rules! double {
($name:expr => $value:expr $(,)?) => {
Tag::Double(Some(String::from($name)), $value)
};
($value:expr $(,)?) => {
Tag::Double(None, $value)
};
}
#[macro_export]
macro_rules! byte_array {
($name:expr => $($value:expr),* $(,)?) => {
Tag::ByteArray(Some(String::from($name)), vec![$($value),*])
};
($($value:expr),* $(,)?) => {
Tag::ByteArray(None, vec![$($value),*])
};
}
#[macro_export]
macro_rules! string {
($name:expr => $value:expr $(,)?) => {
Tag::String(Some(String::from($name)), String::from($value))
};
($value:expr $(,)?) => {
Tag::String(None, String::from($value))
};
}
#[macro_export]
macro_rules! list {
($name:expr => $($value:expr),* $(,)?) => {
Tag::List(Some(String::from($name)), vec![$($value),*])
};
($name:expr => $kind:ty; $($value:expr),* $(,)?) => {
{
macro_rules! eq_type {
($x:ty, $y:ty) => {{
use std::any::TypeId;
TypeId::of::<$x>() == TypeId::of::<$y>()
}};
}
let mut data = vec![];
$(
data.push(
if eq_type!(i8, $kind) {
Tag::Byte(None, $value)
} else if eq_type!(i16, $kind) {
Tag::Short(None, $value)
} else if eq_type!(i32, $kind) {
Tag::Int(None, $value)
} else if eq_type!(i64, $kind) {
Tag::Long(None, $value)
} else {
unimplemented!("{} is an invalid type", std::any::type_name::<$kind>())
}
);
)*
Tag::List(Some(String::from($name)), data)
}
};
($($value:expr),* $(,)?) => {
Tag::List(None, vec![$($value),*])
};
}
#[macro_export]
macro_rules! compound {
($name:expr => $($value:expr),* $(,)?) => {
Tag::Compound(Some(String::from($name)), vec![$($value),*])
};
($($value:expr),* $(,)?) => {
Tag::Compound(None, vec![$($value),*])
};
}
#[macro_export]
macro_rules! int_array {
($name:expr => $($value:expr),* $(,)?) => {
Tag::IntArray(Some(String::from($name)), vec![$($value),*])
};
($($value:expr),* $(,)?) => {
Tag::IntArray(None, vec![$($value),*])
};
}
#[macro_export]
macro_rules! long_array {
($name:expr => $($value:expr),* $(,)?) => {
Tag::LongArray(Some(String::from($name)), vec![$($value),*])
};
($($value:expr),* $(,)?) => {
Tag::LongArray(None, vec![$($value),*])
};
}