nbt/
value.rs

1use std::collections::HashMap;
2use std::fmt;
3use std::io;
4
5use error::{Error, Result};
6use raw::{Endianness, RawWriter, RawReader};
7
8/// Values which can be represented in the Named Binary Tag format.
9#[derive(Clone, Debug, PartialEq)]
10#[cfg_attr(feature = "serde", derive(Serialize))]
11#[cfg_attr(feature = "serde", derive(Deserialize))]
12#[cfg_attr(feature = "serde", serde(untagged))]
13pub enum Value {
14    Byte(i8),
15    Short(i16),
16    Int(i32),
17    Long(i64),
18    Float(f32),
19    Double(f64),
20    ByteArray(Vec<i8>),
21    String(String),
22    List(Vec<Value>),
23    Compound(HashMap<String, Value>),
24    IntArray(Vec<i32>),
25    LongArray(Vec<i64>),
26}
27
28impl Value {
29    /// The type ID of this `Value`, which is a single byte in the range
30    /// `0x01` to `0x0b`.
31    pub fn id(&self) -> i8 {
32        match *self {
33            Value::Byte(_)      => 0x01,
34            Value::Short(_)     => 0x02,
35            Value::Int(_)       => 0x03,
36            Value::Long(_)      => 0x04,
37            Value::Float(_)     => 0x05,
38            Value::Double(_)    => 0x06,
39            Value::ByteArray(_) => 0x07,
40            Value::String(_)    => 0x08,
41            Value::List(_)      => 0x09,
42            Value::Compound(_)  => 0x0a,
43            Value::IntArray(_)  => 0x0b,
44            Value::LongArray(_) => 0x0c,
45        }
46    }
47
48    /// A string representation of this tag.
49    pub fn tag_name(&self) -> &str {
50        match *self {
51            Value::Byte(_)      => "TAG_Byte",
52            Value::Short(_)     => "TAG_Short",
53            Value::Int(_)       => "TAG_Int",
54            Value::Long(_)      => "TAG_Long",
55            Value::Float(_)     => "TAG_Float",
56            Value::Double(_)    => "TAG_Double",
57            Value::ByteArray(_) => "TAG_ByteArray",
58            Value::String(_)    => "TAG_String",
59            Value::List(_)      => "TAG_List",
60            Value::Compound(_)  => "TAG_Compound",
61            Value::IntArray(_)  => "TAG_IntArray",
62            Value::LongArray(_) => "TAG_LongArray",
63        }
64    }
65
66    pub(crate) fn to_raw_writer<W>(&self, dst: &mut RawWriter<W>) -> Result<()>
67        where W: io::Write,
68    {
69        match *self {
70            Value::Byte(val)   => dst.write_bare_byte(val),
71            Value::Short(val)  => dst.write_bare_short(val),
72            Value::Int(val)    => dst.write_bare_int(val),
73            Value::Long(val)   => dst.write_bare_long(val),
74            Value::Float(val)  => dst.write_bare_float(val),
75            Value::Double(val) => dst.write_bare_double(val),
76            Value::ByteArray(ref vals) => dst.write_bare_byte_array(&vals[..]),
77            Value::String(ref val) => dst.write_bare_string(&val),
78            Value::List(ref vals) => {
79                // This is a bit of a trick: if the list is empty, don't bother
80                // checking its type.
81                if vals.len() == 0 {
82                    dst.write_bare_byte(0)?; // TAG_End
83                    dst.write_bare_int(0)?;
84                } else {
85                    // Otherwise, use the first element of the list.
86                    let first_id = vals[0].id();
87                    dst.write_bare_byte(first_id)?;
88                    dst.write_bare_int(vals.len() as i32)?;
89                    for nbt in vals {
90                        // Ensure that all of the tags are the same type.
91                        if nbt.id() != first_id {
92                            return Err(Error::HeterogeneousList);
93                        }
94                        nbt.to_raw_writer(dst)?;
95                    }
96                }
97                Ok(())
98            },
99            Value::Compound(ref vals)  => {
100                for (name, ref nbt) in vals {
101                    // Write the header for the tag.
102                    dst.write_bare_byte(nbt.id())?;
103                    dst.write_bare_string(name)?;
104                    nbt.to_raw_writer(dst)?;
105                }
106                dst.close_nbt()
107            },
108            Value::IntArray(ref vals) => dst.write_bare_int_array(&vals[..]),
109            Value::LongArray(ref vals) => dst.write_bare_long_array(&vals[..]),
110        }
111    }
112
113    /// Writes the payload of this `Value` to an `io::Write` destination.
114    pub fn to_writer<W>(&self, dst: &mut W, endian: Endianness) -> Result<()>
115        where W: io::Write
116    {
117        let mut dst = RawWriter::new(dst, endian);
118        self.to_raw_writer(&mut dst)
119    }
120
121    pub(crate) fn from_raw_reader<R>(id: i8, src: &mut RawReader<R>) -> Result<Value>
122        where R: io::Read,
123    {
124        match id {
125            0x01 => Ok(Value::Byte(src.read_bare_byte()?)),
126            0x02 => Ok(Value::Short(src.read_bare_short()?)),
127            0x03 => Ok(Value::Int(src.read_bare_int()?)),
128            0x04 => Ok(Value::Long(src.read_bare_long()?)),
129            0x05 => Ok(Value::Float(src.read_bare_float()?)),
130            0x06 => Ok(Value::Double(src.read_bare_double()?)),
131            0x07 => Ok(Value::ByteArray(src.read_bare_byte_array()?)),
132            0x08 => Ok(Value::String(src.read_bare_string()?)),
133            0x09 => { // List
134                let id = src.read_bare_byte()?;
135                let len = src.read_bare_int()? as usize;
136                let mut buf = Vec::with_capacity(len);
137                for _ in 0..len {
138                    buf.push(Value::from_raw_reader(id, src)?);
139                }
140                Ok(Value::List(buf))
141            },
142            0x0a => { // Compound
143                let mut buf = HashMap::new();
144                loop {
145                    let (id, name) = src.emit_next_header()?;
146                    if id == 0x00 { break; }
147                    let tag = Value::from_raw_reader(id, src)?;
148                    buf.insert(name, tag);
149                }
150                Ok(Value::Compound(buf))
151            },
152            0x0b => Ok(Value::IntArray(src.read_bare_int_array()?)),
153            0x0c => Ok(Value::LongArray(src.read_bare_long_array()?)),
154            e => Err(Error::InvalidTypeId(e))
155        }
156    }
157
158    /// Reads the payload of an `Value` with a given type ID from an
159    /// `io::Read` source.
160    pub fn from_reader<R>(id: i8, src: &mut R, endian: Endianness) -> Result<Value>
161        where R: io::Read
162    {
163        let mut src = RawReader::new(src, endian);
164        Value::from_raw_reader(id, &mut src)
165    }
166
167    pub fn print(&self, f: &mut fmt::Formatter, offset: usize) -> fmt::Result {
168        match *self {
169            Value::Byte(v)   => write!(f, "{}", v),
170            Value::Short(v)  => write!(f, "{}", v),
171            Value::Int(v)    => write!(f, "{}", v),
172            Value::Long(v)   => write!(f, "{}", v),
173            Value::Float(v)  => write!(f, "{}", v),
174            Value::Double(v) => write!(f, "{}", v),
175            Value::ByteArray(ref v) => write!(f, "{:?}", v),
176            Value::String(ref v) => write!(f, "{}", v),
177            Value::IntArray(ref v) => write!(f, "{:?}", v),
178            Value::LongArray(ref v) => write!(f, "{:?}", v),
179            Value::List(ref v) => {
180                if v.len() == 0 {
181                    write!(f, "zero entries")
182                } else {
183                    write!(f, "{} entries of type {}\n{:>width$}\n", v.len(), v[0].tag_name(), "{", width = offset + 1)?;
184                    for tag in v {
185                        let new_offset = offset + 2;
186                        write!(f, "{:>width$}(None): ", tag.tag_name(), width = new_offset + tag.tag_name().len())?;
187                        tag.print(f, new_offset)?;
188                        write!(f, "\n")?;
189                    }
190                    write!(f, "{:>width$}", "}", width = offset + 1)
191                }
192            }
193            Value::Compound(ref v) => {
194                write!(f, "{} entry(ies)\n{:>width$}\n", v.len(), "{", width = offset + 1)?;
195                for (name, tag) in v {
196                    let new_offset = offset + 2;
197                    write!(f, "{:>width$}({}): ", tag.tag_name(), name, width = new_offset + tag.tag_name().len())?;
198                    tag.print(f, new_offset)?;
199                    write!(f, "\n")?;
200                }
201                write!(f, "{:>width$}", "}", width = offset + 1)
202            }
203        }
204    }
205}
206
207impl fmt::Display for Value {
208    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209        self.print(f, 0)
210    }
211}
212
213impl From<i8> for Value {
214    fn from(t: i8) -> Value { Value::Byte(t) }
215}
216
217impl From<i16> for Value {
218    fn from(t: i16) -> Value { Value::Short(t) }
219}
220
221impl From<i32> for Value {
222    fn from(t: i32) -> Value { Value::Int(t) }
223}
224
225impl From<i64> for Value {
226    fn from(t: i64) -> Value { Value::Long(t) }
227}
228
229impl From<f32> for Value {
230    fn from(t: f32) -> Value { Value::Float(t) }
231}
232
233impl From<f64> for Value {
234    fn from(t: f64) -> Value { Value::Double(t) }
235}
236
237impl<'a> From<&'a str> for Value {
238    fn from(t: &'a str) -> Value { Value::String(t.into()) }
239}
240
241impl From<String> for Value {
242    fn from(t: String) -> Value { Value::String(t) }
243}
244
245impl From<Vec<i8>> for Value {
246    fn from(t: Vec<i8>) -> Value { Value::ByteArray(t) }
247}
248
249impl<'a> From<&'a [i8]> for Value {
250    fn from(t: &'a [i8]) -> Value { Value::ByteArray(t.into()) }
251}
252
253impl From<Vec<i32>> for Value {
254    fn from(t: Vec<i32>) -> Value { Value::IntArray(t) }
255}
256
257impl<'a> From<&'a [i32]> for Value {
258    fn from(t: &'a [i32]) -> Value { Value::IntArray(t.into()) }
259}
260
261impl From<Vec<i64>> for Value {
262    fn from(t: Vec<i64>) -> Value { Value::LongArray(t) }
263}
264
265impl<'a> From<&'a [i64]> for Value {
266    fn from(t: &'a [i64]) -> Value { Value::LongArray(t.into()) }
267}