use crate::{
raw,
snbt::{self, SnbtError},
NbtReprError,
NbtStructureError,
};
use std::{
borrow::{Borrow, BorrowMut, Cow},
convert::{AsMut, AsRef, TryFrom},
fmt::{self, Debug, Display, Formatter},
hash::Hash,
iter::FromIterator,
ops::{Deref, DerefMut, Index, IndexMut},
str::FromStr,
};
#[allow(deprecated)]
use crate::NbtRepr;
#[cfg(feature = "preserve_order")]
pub type Map<T> = indexmap::IndexMap<String, T>;
#[cfg(not(feature = "preserve_order"))]
pub type Map<T> = std::collections::HashMap<String, T>;
#[derive(Clone, PartialEq)]
pub enum NbtTag {
Byte(i8),
Short(i16),
Int(i32),
Long(i64),
Float(f32),
Double(f64),
ByteArray(Vec<i8>),
String(String),
List(NbtList),
Compound(NbtCompound),
IntArray(Vec<i32>),
LongArray(Vec<i64>),
}
impl NbtTag {
pub fn type_specifier(&self) -> Option<&'static str> {
match self {
NbtTag::Byte(_) => Some("B"),
NbtTag::Short(_) => Some("S"),
NbtTag::Long(_) => Some("L"),
NbtTag::Float(_) => Some("F"),
NbtTag::Double(_) => Some("D"),
NbtTag::ByteArray(_) => Some("B"),
NbtTag::IntArray(_) => Some("I"),
NbtTag::LongArray(_) => Some("L"),
_ => None,
}
}
pub(crate) fn tag_name(&self) -> &'static str {
match self {
NbtTag::Byte(_) => "Byte",
NbtTag::Short(_) => "Short",
NbtTag::Int(_) => "Int",
NbtTag::Long(_) => "Long",
NbtTag::Float(_) => "Float",
NbtTag::Double(_) => "Double",
NbtTag::String(_) => "String",
NbtTag::ByteArray(_) => "ByteArray",
NbtTag::IntArray(_) => "IntArray",
NbtTag::LongArray(_) => "LongArray",
NbtTag::Compound(_) => "Compound",
NbtTag::List(_) => "List",
}
}
pub fn to_snbt(&self) -> String {
format!("{:?}", self)
}
pub fn to_pretty_snbt(&self) -> String {
format!("{:#?}", self)
}
#[inline]
pub fn should_quote(string: &str) -> bool {
for ch in string.chars() {
if ch == ':'
|| ch == ','
|| ch == '"'
|| ch == '\''
|| ch == '{'
|| ch == '}'
|| ch == '['
|| ch == ']'
|| ch == '\\'
|| ch == '\n'
|| ch == '\r'
|| ch == '\t'
{
return true;
}
}
false
}
pub fn string_to_snbt(string: &str) -> Cow<'_, str> {
if !Self::should_quote(string) {
return Cow::Borrowed(string);
}
let surrounding: char;
if string.contains('"') {
surrounding = '\'';
} else {
surrounding = '"';
}
let mut snbt_string = String::with_capacity(2 + string.len());
snbt_string.push(surrounding);
for ch in string.chars() {
match ch {
'\n' => {
snbt_string.push_str("\\n");
continue;
}
'\r' => {
snbt_string.push_str("\\r");
continue;
}
'\t' => {
snbt_string.push_str("\\t");
continue;
}
_ =>
if ch == surrounding || ch == '\\' {
snbt_string.push('\\');
},
}
snbt_string.push(ch);
}
snbt_string.push(surrounding);
Cow::Owned(snbt_string)
}
#[allow(clippy::write_with_newline)]
fn to_formatted_snbt(&self, indent: &mut String, f: &mut Formatter<'_>) -> fmt::Result {
fn write_list(
list: &[impl Display],
indent: &mut String,
ts: &str,
f: &mut Formatter<'_>,
) -> fmt::Result {
if list.is_empty() {
return write!(f, "[{};]", ts);
}
if f.alternate() {
indent.push_str(" ");
write!(f, "[\n{}{};\n", indent, ts)?;
} else {
write!(f, "[{};", ts)?;
}
let last_index = list.len() - 1;
for (index, element) in list.iter().enumerate() {
if f.alternate() {
write!(f, "{}", indent)?;
}
Display::fmt(element, f)?;
if index != last_index {
if f.alternate() {
write!(f, ",\n")?;
} else {
write!(f, ",")?;
}
}
}
if f.alternate() {
indent.truncate(indent.len() - 4);
write!(f, "\n{}]", &indent)
} else {
write!(f, "]")
}
}
#[inline]
fn write(value: &impl Display, ts: Option<&str>, f: &mut Formatter<'_>) -> fmt::Result {
match ts {
Some(ts) => {
Display::fmt(value, f)?;
write!(f, "{}", ts)
}
None => Display::fmt(value, f),
}
}
let ts = self.type_specifier();
match self {
NbtTag::Byte(value) => write(value, ts, f),
NbtTag::Short(value) => write(value, ts, f),
NbtTag::Int(value) => write(value, ts, f),
NbtTag::Long(value) => write(value, ts, f),
NbtTag::Float(value) => write(value, ts, f),
NbtTag::Double(value) => write(value, ts, f),
NbtTag::ByteArray(value) => write_list(&**value, indent, ts.unwrap(), f),
NbtTag::String(value) => write!(f, "{}", Self::string_to_snbt(value)),
NbtTag::List(value) => value.to_formatted_snbt(indent, f),
NbtTag::Compound(value) => value.to_formatted_snbt(indent, f),
NbtTag::IntArray(value) => write_list(&**value, indent, ts.unwrap(), f),
NbtTag::LongArray(value) => write_list(&**value, indent, ts.unwrap(), f),
}
}
}
impl Display for NbtTag {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.to_formatted_snbt(&mut String::new(), f)
}
}
impl Debug for NbtTag {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.to_formatted_snbt(&mut String::new(), f)
}
}
macro_rules! tag_from {
($($type:ty, $tag:ident);*) => {
$(
impl From<$type> for NbtTag {
#[inline]
fn from(value: $type) -> NbtTag {
NbtTag::$tag(value)
}
}
)*
};
}
tag_from!(
i8, Byte;
i16, Short;
i32, Int;
i64, Long;
f32, Float;
f64, Double;
Vec<i8>, ByteArray;
String, String;
NbtList, List;
NbtCompound, Compound;
Vec<i32>, IntArray;
Vec<i64>, LongArray
);
impl From<&str> for NbtTag {
#[inline]
fn from(value: &str) -> NbtTag {
NbtTag::String(value.to_owned())
}
}
impl From<&String> for NbtTag {
#[inline]
fn from(value: &String) -> NbtTag {
NbtTag::String(value.clone())
}
}
impl From<bool> for NbtTag {
#[inline]
fn from(value: bool) -> NbtTag {
NbtTag::Byte(if value { 1 } else { 0 })
}
}
impl From<u8> for NbtTag {
#[inline]
fn from(value: u8) -> Self {
NbtTag::Byte(value as i8)
}
}
impl From<Vec<u8>> for NbtTag {
#[inline]
fn from(value: Vec<u8>) -> Self {
NbtTag::ByteArray(raw::cast_byte_buf_to_signed(value))
}
}
#[allow(deprecated)]
impl<T: NbtRepr> From<T> for NbtTag {
#[inline]
fn from(x: T) -> Self {
NbtTag::Compound(x.to_nbt())
}
}
macro_rules! prim_from_tag {
($($type:ty, $tag:ident);*) => {
$(
impl TryFrom<&NbtTag> for $type {
type Error = NbtStructureError;
#[inline]
fn try_from(tag: &NbtTag) -> Result<Self, Self::Error> {
if let NbtTag::$tag(value) = tag {
Ok(*value)
} else {
Err(NbtStructureError::type_mismatch(stringify!($tag), tag.tag_name()))
}
}
}
)*
};
}
prim_from_tag!(
i8, Byte;
i16, Short;
i32, Int;
i64, Long;
f32, Float;
f64, Double
);
impl TryFrom<&NbtTag> for bool {
type Error = NbtStructureError;
fn try_from(tag: &NbtTag) -> Result<Self, Self::Error> {
match *tag {
NbtTag::Byte(value) => Ok(value != 0),
NbtTag::Short(value) => Ok(value != 0),
NbtTag::Int(value) => Ok(value != 0),
NbtTag::Long(value) => Ok(value != 0),
_ => Err(NbtStructureError::type_mismatch(
"Byte, Short, Int, or Long",
tag.tag_name(),
)),
}
}
}
impl TryFrom<&NbtTag> for u8 {
type Error = NbtStructureError;
#[inline]
fn try_from(tag: &NbtTag) -> Result<Self, Self::Error> {
match *tag {
NbtTag::Byte(value) => Ok(value as u8),
_ => Err(NbtStructureError::type_mismatch("Byte", tag.tag_name())),
}
}
}
macro_rules! ref_from_tag {
($($type:ty, $tag:ident);*) => {
$(
impl<'a> TryFrom<&'a NbtTag> for &'a $type {
type Error = NbtStructureError;
#[inline]
fn try_from(tag: &'a NbtTag) -> Result<Self, Self::Error> {
if let NbtTag::$tag(value) = tag {
Ok(value)
} else {
Err(NbtStructureError::type_mismatch(stringify!($tag), tag.tag_name()))
}
}
}
impl<'a> TryFrom<&'a mut NbtTag> for &'a mut $type {
type Error = NbtStructureError;
#[inline]
fn try_from(tag: &'a mut NbtTag) -> Result<Self, Self::Error> {
if let NbtTag::$tag(value) = tag {
Ok(value)
} else {
Err(NbtStructureError::type_mismatch(stringify!($tag), tag.tag_name()))
}
}
}
)*
};
}
ref_from_tag!(
i8, Byte;
i16, Short;
i32, Int;
i64, Long;
f32, Float;
f64, Double;
Vec<i8>, ByteArray;
[i8], ByteArray;
String, String;
str, String;
NbtList, List;
NbtCompound, Compound;
Vec<i32>, IntArray;
[i32], IntArray;
Vec<i64>, LongArray;
[i64], LongArray
);
impl<'a> TryFrom<&'a NbtTag> for &'a u8 {
type Error = NbtStructureError;
#[inline]
fn try_from(tag: &'a NbtTag) -> Result<Self, Self::Error> {
if let NbtTag::Byte(value) = tag {
Ok(unsafe { &*(value as *const i8 as *const u8) })
} else {
Err(NbtStructureError::type_mismatch("Byte", tag.tag_name()))
}
}
}
impl<'a> TryFrom<&'a NbtTag> for &'a [u8] {
type Error = NbtStructureError;
#[inline]
fn try_from(tag: &'a NbtTag) -> Result<Self, Self::Error> {
if let NbtTag::ByteArray(value) = tag {
Ok(raw::cast_bytes_to_unsigned(value.as_slice()))
} else {
Err(NbtStructureError::type_mismatch(
"ByteArray",
tag.tag_name(),
))
}
}
}
macro_rules! from_tag {
($($type:ty, $tag:ident);*) => {
$(
impl TryFrom<NbtTag> for $type {
type Error = NbtStructureError;
#[inline]
fn try_from(tag: NbtTag) -> Result<Self, Self::Error> {
if let NbtTag::$tag(value) = tag {
Ok(value)
} else {
Err(NbtStructureError::type_mismatch(stringify!($tag), tag.tag_name()))
}
}
}
)*
};
}
from_tag!(
i8, Byte;
i16, Short;
i32, Int;
i64, Long;
f32, Float;
f64, Double;
Vec<i8>, ByteArray;
String, String;
NbtList, List;
NbtCompound, Compound;
Vec<i32>, IntArray;
Vec<i64>, LongArray
);
impl TryFrom<NbtTag> for Vec<u8> {
type Error = NbtStructureError;
#[inline]
fn try_from(tag: NbtTag) -> Result<Self, Self::Error> {
if let NbtTag::ByteArray(value) = tag {
Ok(raw::cast_byte_buf_to_unsigned(value))
} else {
Err(NbtStructureError::type_mismatch(
"ByteArray",
tag.tag_name(),
))
}
}
}
#[repr(transparent)]
#[derive(Clone, PartialEq)]
pub struct NbtList(pub(crate) Vec<NbtTag>);
impl NbtList {
#[inline]
pub const fn new() -> Self {
NbtList(Vec::new())
}
#[inline]
pub fn inner_mut(&mut self) -> &mut Vec<NbtTag> {
&mut self.0
}
#[inline]
pub fn into_inner(self) -> Vec<NbtTag> {
self.0
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
NbtList(Vec::with_capacity(capacity))
}
#[inline]
pub fn clone_from<'a, T, L>(list: L) -> Self
where
T: Clone + Into<NbtTag> + 'a,
L: IntoIterator<Item = &'a T>,
{
NbtList(list.into_iter().map(|x| x.clone().into()).collect())
}
#[inline]
#[deprecated(
since = "0.2.3",
note = "This method will eventually be made obsolete with serde compatibility"
)]
#[allow(deprecated)]
pub fn clone_repr_from<'a, T, L>(list: L) -> Self
where
T: NbtRepr + 'a,
L: IntoIterator<Item = &'a T>,
{
NbtList(list.into_iter().map(|x| x.to_nbt().into()).collect())
}
#[inline]
pub fn iter_map<'a, T: TryFrom<&'a NbtTag>>(
&'a self,
) -> impl Iterator<Item = Result<T, <T as TryFrom<&'a NbtTag>>::Error>> + 'a {
self.0.iter().map(|tag| T::try_from(tag))
}
#[inline]
pub fn iter_mut_map<'a, T: TryFrom<&'a mut NbtTag>>(
&'a mut self,
) -> impl Iterator<Item = Result<T, <T as TryFrom<&'a mut NbtTag>>::Error>> + 'a {
self.0.iter_mut().map(|tag| T::try_from(tag))
}
pub fn to_snbt(&self) -> String {
format!("{:?}", self)
}
pub fn to_pretty_snbt(&self) -> String {
format!("{:#?}", self)
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn get<'a, T>(&'a self, index: usize) -> Result<T, NbtReprError>
where
T: TryFrom<&'a NbtTag>,
T::Error: Into<anyhow::Error>,
{
T::try_from(
self.0
.get(index)
.ok_or_else(|| NbtStructureError::invalid_index(index, self.len()))?,
)
.map_err(NbtReprError::from_any)
}
#[inline]
pub fn get_mut<'a, T>(&'a mut self, index: usize) -> Result<T, NbtReprError>
where
T: TryFrom<&'a mut NbtTag>,
T::Error: Into<anyhow::Error>,
{
let len = self.len();
T::try_from(
self.0
.get_mut(index)
.ok_or_else(|| NbtStructureError::invalid_index(index, len))?,
)
.map_err(NbtReprError::from_any)
}
#[inline]
pub fn push<T: Into<NbtTag>>(&mut self, value: T) {
self.0.push(value.into());
}
#[allow(clippy::write_with_newline)]
fn to_formatted_snbt(&self, indent: &mut String, f: &mut Formatter<'_>) -> fmt::Result {
if self.is_empty() {
return write!(f, "[]");
}
if f.alternate() {
indent.push_str(" ");
write!(f, "[\n")?;
} else {
write!(f, "[")?;
}
let last_index = self.len() - 1;
for (index, element) in self.0.iter().enumerate() {
if f.alternate() {
write!(f, "{}", indent)?;
}
element.to_formatted_snbt(indent, f)?;
if index != last_index {
if f.alternate() {
write!(f, ",\n")?;
} else {
write!(f, ",")?;
}
}
}
if f.alternate() {
indent.truncate(indent.len() - 4);
write!(f, "\n{}]", indent)
} else {
write!(f, "]")
}
}
}
impl Default for NbtList {
#[inline]
fn default() -> Self {
NbtList::new()
}
}
impl<T: Into<NbtTag>> From<Vec<T>> for NbtList {
#[inline]
fn from(list: Vec<T>) -> Self {
NbtList(list.into_iter().map(|x| x.into()).collect())
}
}
impl IntoIterator for NbtList {
type IntoIter = <Vec<NbtTag> as IntoIterator>::IntoIter;
type Item = NbtTag;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a NbtList {
type IntoIter = <&'a Vec<NbtTag> as IntoIterator>::IntoIter;
type Item = &'a NbtTag;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a> IntoIterator for &'a mut NbtList {
type IntoIter = <&'a mut Vec<NbtTag> as IntoIterator>::IntoIter;
type Item = &'a mut NbtTag;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}
impl FromIterator<NbtTag> for NbtList {
#[inline]
fn from_iter<T: IntoIterator<Item = NbtTag>>(iter: T) -> Self {
NbtList(Vec::from_iter(iter))
}
}
impl AsRef<[NbtTag]> for NbtList {
#[inline]
fn as_ref(&self) -> &[NbtTag] {
&self.0
}
}
impl AsMut<[NbtTag]> for NbtList {
#[inline]
fn as_mut(&mut self) -> &mut [NbtTag] {
&mut self.0
}
}
impl Borrow<[NbtTag]> for NbtList {
#[inline]
fn borrow(&self) -> &[NbtTag] {
&self.0
}
}
impl BorrowMut<[NbtTag]> for NbtList {
#[inline]
fn borrow_mut(&mut self) -> &mut [NbtTag] {
&mut self.0
}
}
impl Deref for NbtList {
type Target = [NbtTag];
#[inline]
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl DerefMut for NbtList {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.0
}
}
impl Display for NbtList {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.to_formatted_snbt(&mut String::new(), f)
}
}
impl Debug for NbtList {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.to_formatted_snbt(&mut String::new(), f)
}
}
impl Extend<NbtTag> for NbtList {
#[inline]
fn extend<T: IntoIterator<Item = NbtTag>>(&mut self, iter: T) {
self.0.extend(iter);
}
}
impl Index<usize> for NbtList {
type Output = NbtTag;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl IndexMut<usize> for NbtList {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.0[index]
}
}
#[repr(transparent)]
#[derive(Clone, PartialEq)]
pub struct NbtCompound(pub(crate) Map<NbtTag>);
impl NbtCompound {
#[inline]
pub fn new() -> Self {
NbtCompound(Map::new())
}
#[inline]
pub fn inner(&self) -> &Map<NbtTag> {
&self.0
}
#[inline]
pub fn inner_mut(&mut self) -> &mut Map<NbtTag> {
&mut self.0
}
#[inline]
pub fn into_inner(self) -> Map<NbtTag> {
self.0
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
NbtCompound(Map::with_capacity(capacity))
}
#[inline]
pub fn clone_from<'a, K, V, M>(map: &'a M) -> Self
where
K: Clone + Into<String> + 'a,
V: Clone + Into<NbtTag> + 'a,
&'a M: IntoIterator<Item = (&'a K, &'a V)>,
{
NbtCompound(
map.into_iter()
.map(|(key, value)| (key.clone().into(), value.clone().into()))
.collect(),
)
}
#[inline]
#[deprecated(
since = "0.2.3",
note = "This method will eventually be made obsolete with serde compatibility"
)]
#[allow(deprecated)]
pub fn clone_repr_from<'a, K, V, M>(map: &'a M) -> Self
where
K: Clone + Into<String> + 'a,
V: NbtRepr + 'a,
&'a M: IntoIterator<Item = (&'a K, &'a V)>,
{
NbtCompound(
map.into_iter()
.map(|(key, value)| (key.clone().into(), value.to_nbt().into()))
.collect(),
)
}
#[inline]
pub fn iter_map<'a, T: TryFrom<&'a NbtTag>>(
&'a self,
) -> impl Iterator<Item = (&'a str, Result<T, <T as TryFrom<&'a NbtTag>>::Error>)> + 'a {
self.0
.iter()
.map(|(key, tag)| (key.as_str(), T::try_from(tag)))
}
#[inline]
pub fn iter_mut_map<'a, T: TryFrom<&'a mut NbtTag>>(
&'a mut self,
) -> impl Iterator<Item = (&'a str, Result<T, <T as TryFrom<&'a mut NbtTag>>::Error>)> + 'a
{
self.0
.iter_mut()
.map(|(key, tag)| (key.as_str(), T::try_from(tag)))
}
pub fn to_snbt(&self) -> String {
format!("{:?}", self)
}
pub fn to_pretty_snbt(&self) -> String {
format!("{:#?}", self)
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn get<'a, 'b, K, T>(&'a self, name: &'b K) -> Result<T, NbtReprError>
where
String: Borrow<K>,
K: Hash + Eq + ?Sized,
&'b K: Into<String>,
T: TryFrom<&'a NbtTag>,
T::Error: Into<anyhow::Error>,
{
T::try_from(
self.0
.get(name)
.ok_or_else(|| NbtStructureError::missing_tag(name))?,
)
.map_err(NbtReprError::from_any)
}
#[inline]
pub fn get_mut<'a, 'b, K, T>(&'a mut self, name: &'b K) -> Result<T, NbtReprError>
where
String: Borrow<K>,
K: Hash + Eq + ?Sized,
&'b K: Into<String>,
T: TryFrom<&'a mut NbtTag>,
T::Error: Into<anyhow::Error>,
{
T::try_from(
self.0
.get_mut(name)
.ok_or_else(|| NbtStructureError::missing_tag(name))?,
)
.map_err(NbtReprError::from_any)
}
#[inline]
pub fn contains_key<K>(&self, key: &K) -> bool
where
String: Borrow<K>,
K: Hash + Eq + ?Sized,
{
self.0.contains_key(key)
}
#[inline]
pub fn insert<K: Into<String>, T: Into<NbtTag>>(&mut self, name: K, value: T) {
self.0.insert(name.into(), value.into());
}
#[inline]
pub fn from_snbt(input: &str) -> Result<Self, SnbtError> {
snbt::parse(input)
}
#[allow(clippy::write_with_newline)]
fn to_formatted_snbt(&self, indent: &mut String, f: &mut Formatter<'_>) -> fmt::Result {
if self.is_empty() {
return write!(f, "{{}}");
}
if f.alternate() {
indent.push_str(" ");
write!(f, "{{\n")?;
} else {
write!(f, "{{")?;
}
let last_index = self.len() - 1;
for (index, (key, value)) in self.0.iter().enumerate() {
let key = NbtTag::string_to_snbt(key);
if f.alternate() {
write!(f, "{}{}: ", indent, key)?;
} else {
write!(f, "{}:", key)?;
}
value.to_formatted_snbt(indent, f)?;
if index != last_index {
if f.alternate() {
write!(f, ",\n")?;
} else {
write!(f, ",")?;
}
}
}
if f.alternate() {
indent.truncate(indent.len() - 4);
write!(f, "\n{}}}", indent)
} else {
write!(f, "}}")
}
}
}
impl Default for NbtCompound {
#[inline]
fn default() -> Self {
NbtCompound::new()
}
}
impl IntoIterator for NbtCompound {
type IntoIter = <Map<NbtTag> as IntoIterator>::IntoIter;
type Item = <Map<NbtTag> as IntoIterator>::Item;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a NbtCompound {
type IntoIter = <&'a Map<NbtTag> as IntoIterator>::IntoIter;
type Item = <&'a Map<NbtTag> as IntoIterator>::Item;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a> IntoIterator for &'a mut NbtCompound {
type IntoIter = <&'a mut Map<NbtTag> as IntoIterator>::IntoIter;
type Item = (&'a String, &'a mut NbtTag);
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}
impl FromIterator<(String, NbtTag)> for NbtCompound {
#[inline]
fn from_iter<T: IntoIterator<Item = (String, NbtTag)>>(iter: T) -> Self {
NbtCompound(Map::from_iter(iter))
}
}
impl<Q: ?Sized> Index<&Q> for NbtCompound
where
String: Borrow<Q>,
Q: Eq + Hash,
{
type Output = NbtTag;
#[inline]
fn index(&self, key: &Q) -> &NbtTag {
&self.0[key]
}
}
impl FromStr for NbtCompound {
type Err = SnbtError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_snbt(s)
}
}
impl Display for NbtCompound {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.to_formatted_snbt(&mut String::new(), f)
}
}
impl Debug for NbtCompound {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.to_formatted_snbt(&mut String::new(), f)
}
}
impl Extend<(String, NbtTag)> for NbtCompound {
#[inline]
fn extend<T: IntoIterator<Item = (String, NbtTag)>>(&mut self, iter: T) {
self.0.extend(iter);
}
}
#[cfg(feature = "serde")]
pub use serde_impl::*;
#[cfg(feature = "serde")]
mod serde_impl {
use super::*;
use crate::serde::{Array, TypeHint};
use serde::{
de::{self, MapAccess, Visitor},
Deserialize,
Deserializer,
Serialize,
Serializer,
};
impl Serialize for NbtTag {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
match self {
&NbtTag::Byte(value) => serializer.serialize_i8(value),
&NbtTag::Short(value) => serializer.serialize_i16(value),
&NbtTag::Int(value) => serializer.serialize_i32(value),
&NbtTag::Long(value) => serializer.serialize_i64(value),
&NbtTag::Float(value) => serializer.serialize_f32(value),
&NbtTag::Double(value) => serializer.serialize_f64(value),
NbtTag::ByteArray(array) => Array::from(array).serialize(serializer),
NbtTag::String(value) => serializer.serialize_str(value),
NbtTag::List(list) => list.serialize(serializer),
NbtTag::Compound(compound) => compound.serialize(serializer),
NbtTag::IntArray(array) => Array::from(array).serialize(serializer),
NbtTag::LongArray(array) => Array::from(array).serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for NbtTag {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
deserializer.deserialize_any(NbtTagVisitor)
}
}
struct NbtTagVisitor;
impl<'de> Visitor<'de> for NbtTagVisitor {
type Value = NbtTag;
fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "a valid NBT type")
}
#[inline]
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::Byte(if v { 1 } else { 0 }))
}
#[inline]
fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::Byte(v))
}
#[inline]
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::Byte(v as i8))
}
#[inline]
fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::Short(v))
}
#[inline]
fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::Int(v))
}
#[inline]
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::Long(v))
}
#[inline]
fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::Float(v))
}
#[inline]
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::Double(v))
}
#[inline]
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where E: de::Error {
self.visit_byte_buf(v.to_owned())
}
#[inline]
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::ByteArray(raw::cast_byte_buf_to_signed(v)))
}
#[inline]
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::String(v.to_owned()))
}
#[inline]
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where E: de::Error {
Ok(NbtTag::String(v))
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where A: MapAccess<'de> {
let mut dest = match map.size_hint() {
Some(hint) => Map::with_capacity(hint),
None => Map::new(),
};
while let Some((key, tag)) = map.next_entry::<String, NbtTag>()? {
dest.insert(key, tag);
}
Ok(NbtTag::Compound(NbtCompound(dest)))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where A: de::SeqAccess<'de> {
enum ArbitraryList {
Byte(Vec<i8>),
Int(Vec<i32>),
Long(Vec<i64>),
Tag(Vec<NbtTag>),
Indeterminate,
}
impl ArbitraryList {
fn into_tag(self) -> NbtTag {
match self {
ArbitraryList::Byte(list) => NbtTag::ByteArray(list),
ArbitraryList::Int(list) => NbtTag::IntArray(list),
ArbitraryList::Long(list) => NbtTag::LongArray(list),
ArbitraryList::Tag(list) => NbtTag::List(NbtList(list)),
ArbitraryList::Indeterminate => NbtTag::List(NbtList::new()),
}
}
}
let mut list = ArbitraryList::Indeterminate;
fn init_vec<T>(element: T, size: Option<usize>) -> Vec<T> {
match size {
Some(size) => {
let mut vec = Vec::with_capacity(1 + size);
vec.push(element);
vec
}
None => vec![element],
}
}
while let Some(tag) = seq.next_element::<NbtTag>()? {
match (tag, &mut list) {
(NbtTag::Byte(value), ArbitraryList::Byte(list)) => list.push(value),
(NbtTag::Int(value), ArbitraryList::Int(list)) => list.push(value),
(NbtTag::Long(value), ArbitraryList::Long(list)) => list.push(value),
(tag, ArbitraryList::Tag(list)) => list.push(tag),
(tag, list @ ArbitraryList::Indeterminate) => {
let size = seq.size_hint();
match tag {
NbtTag::Byte(value) =>
*list = ArbitraryList::Byte(init_vec(value, size)),
NbtTag::Int(value) => *list = ArbitraryList::Int(init_vec(value, size)),
NbtTag::Long(value) =>
*list = ArbitraryList::Long(init_vec(value, size)),
tag => *list = ArbitraryList::Tag(init_vec(tag, size)),
}
}
_ =>
return Err(de::Error::custom(
"tag type mismatch when deserializing array",
)),
}
}
match seq.next_element::<TypeHint>() {
Ok(Some(TypeHint { hint: Some(tag_id) })) => match (list, tag_id) {
(ArbitraryList::Byte(list), 0x9) => Ok(NbtTag::List(NbtList(
list.into_iter().map(Into::into).collect(),
))),
(ArbitraryList::Int(list), 0x9) => Ok(NbtTag::List(NbtList(
list.into_iter().map(Into::into).collect(),
))),
(ArbitraryList::Long(list), 0x9) => Ok(NbtTag::List(NbtList(
list.into_iter().map(Into::into).collect(),
))),
(ArbitraryList::Indeterminate, 0x7) => Ok(NbtTag::ByteArray(Vec::new())),
(ArbitraryList::Indeterminate, 0xB) => Ok(NbtTag::IntArray(Vec::new())),
(ArbitraryList::Indeterminate, 0xC) => Ok(NbtTag::LongArray(Vec::new())),
(list, _) => Ok(list.into_tag()),
},
_ => Ok(list.into_tag()),
}
}
}
impl Serialize for NbtList {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for NbtList {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
Ok(NbtList(Deserialize::deserialize(deserializer)?))
}
}
impl Serialize for NbtCompound {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for NbtCompound {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
Ok(NbtCompound(Deserialize::deserialize(deserializer)?))
}
}
}