bookshelf/nbt/
mod.rs

1/*
2    MIT License
3
4    Copyright (c) 2022 Valence
5
6    Permission is hereby granted, free of charge, to any person obtaining a copy
7    of this software and associated documentation files (the "Software"), to deal
8    in the Software without restriction, including without limitation the rights
9    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10    copies of the Software, and to permit persons to whom the Software is
11    furnished to do so, subject to the following conditions:
12
13    The above copyright notice and this permission notice shall be included in all
14    copies or substantial portions of the Software.
15
16    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22    SOFTWARE.
23
24    https://github.com/valence-rs/serde_nbt
25 */
26
27use serde::{de,ser};
28use std::{
29    fmt::{Display, self}, io, iter::FusedIterator
30};
31
32/// Module for reading and writing binary NBT streams, can be used for files and packets.
33pub mod bin;
34
35mod tag;
36pub use tag::{Tag, Compound, List, TagConversionError, CompoundTryGetInto, byte_array, int_array, long_array};
37pub(crate) use tag::{ARRAY_ENUM_NAME, BYTE_ARRAY_NAME, INT_ARRAY_NAME, LONG_ARRAY_NAME};
38
39/// Represents the different NBT Tag Types
40#[derive(Clone, PartialEq, Debug, Copy)]
41pub enum TagType {
42    End,
43    Byte,
44    Short,
45    Int,
46    Long,
47    Float,
48    Double,
49    ByteArray,
50    String,
51    List,
52    Compound,
53    IntArray,
54    LongArray,
55}
56
57impl TagType {
58    /// Turns a byte into it's corresponding NBT Tag Type
59    pub fn from_u8(id: u8) -> Result<TagType> {
60        match id {
61            0x00 => Ok(TagType::End),
62            0x01 => Ok(TagType::Byte),
63            0x02 => Ok(TagType::Short),
64            0x03 => Ok(TagType::Int),
65            0x04 => Ok(TagType::Long),
66            0x05 => Ok(TagType::Float),
67            0x06 => Ok(TagType::Double),
68            0x07 => Ok(TagType::ByteArray),
69            0x08 => Ok(TagType::String),
70            0x09 => Ok(TagType::List),
71            0x0A => Ok(TagType::Compound),
72            0x0B => Ok(TagType::IntArray),
73            0x0C => Ok(TagType::LongArray),
74            _ => Err(Error::new_owned(
75                format!("{} is an invalid NBT tag id", id)
76            )),
77        }
78    }
79}
80
81impl Display for TagType {
82    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83        match &self {
84            TagType::End => write!(f, "TAG_End"),
85            TagType::Byte => write!(f, "TAG_Byte"),
86            TagType::Short => write!(f, "TAG_Short"),
87            TagType::Int => write!(f, "TAG_Int"),
88            TagType::Long => write!(f, "TAG_Long"),
89            TagType::Float => write!(f, "TAG_Float"),
90            TagType::Double => write!(f, "TAG_Double"),
91            TagType::ByteArray => write!(f, "TAG_Byte_Array"),
92            TagType::String => write!(f, "TAG_String"),
93            TagType::List => write!(f, "TAG_List"),
94            TagType::Compound => write!(f, "TAG_Compound"),
95            TagType::IntArray => write!(f, "TAG_Int_Array"),
96            TagType::LongArray => write!(f, "TAG_Long_Array")
97        }
98    }
99}
100
101pub type Result<T> = std::result::Result<T, Error>;
102
103/// An error for serializing and deserializing NBT,
104/// provides a back trace to the field that caused the error.
105#[derive(Debug)]
106pub struct Error {
107    /// Box this to keep the error as small as possible. We don't want to
108    /// slow down the common case where no error occurs.
109    inner: Box<ErrorInner>,
110}
111
112#[derive(Debug)]
113struct ErrorInner {
114    trace: Vec<String>,
115    cause: Cause,
116}
117
118#[derive(Debug)]
119enum Cause {
120    Io(io::Error),
121    Mutf8(mutf8::Error),
122    // catch-all errors
123    Owned(Box<str>),
124    Static(&'static str),
125}
126
127impl Error {
128    pub(crate) fn new_owned(msg: impl Into<Box<str>>) -> Self {
129        Self {
130            inner: Box::new(ErrorInner {
131                trace: Vec::new(),
132                cause: Cause::Owned(msg.into()),
133            }),
134        }
135    }
136
137    pub(crate) fn new_static(msg: &'static str) -> Self {
138        Self {
139            inner: Box::new(ErrorInner {
140                trace: Vec::new(),
141                cause: Cause::Static(msg),
142            }),
143        }
144    }
145
146    pub(crate) fn field(mut self, ctx: impl Into<String>) -> Self {
147        self.inner.trace.push(ctx.into());
148        self
149    }
150
151    /// Returns an iterator through the nested fields of an NBT compound to the
152    /// location where the error occurred.
153    ///
154    /// The iterator's `Item` is the name of the current field.
155    pub fn trace(
156        &self,
157    ) -> impl DoubleEndedIterator<Item = &str> + ExactSizeIterator + FusedIterator + Clone + '_
158    {
159        self.inner.trace.iter().rev().map(|s| s.as_str())
160    }
161}
162
163impl Display for Error {
164    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165        let len = self.inner.trace.len();
166
167        if len > 0 {
168            write!(f, "(")?;
169            for (i, ctx) in self.trace().enumerate() {
170                write!(f, "{ctx}")?;
171
172                if i != len - 1 {
173                    write!(f, " → ")?;
174                }
175            }
176            write!(f, ") ")?;
177        }
178
179        match &self.inner.cause {
180            Cause::Io(e) => e.fmt(f),
181            Cause::Mutf8(e) => e.fmt(f),
182            Cause::Owned(s) => write!(f, "{s}"),
183            Cause::Static(s) => write!(f, "{s}"),
184        }
185    }
186}
187
188impl std::error::Error for Error {
189    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
190        match &self.inner.cause {
191            Cause::Io(e) => Some(e),
192            Cause::Mutf8(e) => Some(e),
193            Cause::Owned(_) => None,
194            Cause::Static(_) => None,
195        }
196    }
197}
198
199impl ser::Error for Error {
200    fn custom<T>(msg: T) -> Self
201    where
202        T: Display,
203    {
204        Error::new_owned(format!("{msg}"))
205    }
206}
207
208impl de::Error for Error {
209    fn custom<T>(msg: T) -> Self
210    where
211        T: Display,
212    {
213        Error::new_owned(format!("{msg}"))
214    }
215}
216
217impl From<io::Error> for Error {
218    fn from(e: io::Error) -> Self {
219        Self {
220            inner: Box::new(ErrorInner {
221                trace: Vec::new(),
222                cause: Cause::Io(e),
223            }),
224        }
225    }
226}
227
228impl From<mutf8::Error> for Error {
229    fn from(e: mutf8::Error) -> Self {
230        Self {
231            inner: Box::new(ErrorInner {
232                trace: Vec::new(),
233                cause: Cause::Mutf8(e),
234            }),
235        }
236    }
237}