use crate::Map;
use std::fmt;
use std::io;
use std::ops::Index;
use byteorder::WriteBytesExt;
use flate2::read::{GzDecoder, ZlibDecoder};
use flate2::write::{GzEncoder, ZlibEncoder};
use flate2::Compression;
use error::{Error, Result};
use raw;
use value::Value;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Blob {
title: String,
content: Map<String, Value>,
}
impl Blob {
pub fn new() -> Blob {
Blob {
title: "".to_string(),
content: Map::new(),
}
}
pub fn named<S>(name: S) -> Blob
where
S: Into<String>,
{
Blob {
title: name.into(),
content: Map::new(),
}
}
pub fn from_reader<R>(src: &mut R) -> Result<Blob>
where
R: io::Read,
{
let (tag, title) = raw::emit_next_header(src)?;
if tag != 0x0a {
return Err(Error::NoRootCompound);
}
let content = Value::from_reader(tag, src)?;
match content {
Value::Compound(map) => Ok(Blob {
title,
content: map,
}),
_ => Err(Error::NoRootCompound),
}
}
pub fn from_gzip_reader<R>(src: &mut R) -> Result<Blob>
where
R: io::Read,
{
let mut data = GzDecoder::new(src);
Blob::from_reader(&mut data)
}
pub fn from_zlib_reader<R>(src: &mut R) -> Result<Blob>
where
R: io::Read,
{
Blob::from_reader(&mut ZlibDecoder::new(src))
}
pub fn to_writer<W>(&self, mut dst: &mut W) -> Result<()>
where
W: io::Write,
{
dst.write_u8(0x0a)?;
raw::write_bare_string(&mut dst, &self.title)?;
for (name, ref nbt) in self.content.iter() {
dst.write_u8(nbt.id())?;
raw::write_bare_string(&mut dst, name)?;
nbt.to_writer(&mut dst)?;
}
raw::close_nbt(&mut dst)
}
pub fn to_gzip_writer<W>(&self, dst: &mut W) -> Result<()>
where
W: io::Write,
{
self.to_writer(&mut GzEncoder::new(dst, Compression::default()))
}
pub fn to_zlib_writer<W>(&self, dst: &mut W) -> Result<()>
where
W: io::Write,
{
self.to_writer(&mut ZlibEncoder::new(dst, Compression::default()))
}
pub fn insert<S, V>(&mut self, name: S, value: V) -> Result<()>
where
S: Into<String>,
V: Into<Value>,
{
let nvalue = value.into();
if let Value::List(ref vals) = nvalue {
if !vals.is_empty() {
let first_id = vals[0].id();
for nbt in vals {
if nbt.id() != first_id {
return Err(Error::HeterogeneousList);
}
}
}
}
self.content.insert(name.into(), nvalue);
Ok(())
}
pub fn get<S>(&self, name: S) -> Option<&Value>
where
S: Into<&'static str>,
{
self.content.get(name.into())
}
pub fn len_bytes(&self) -> usize {
4 + self.title.len()
+ self
.content
.iter()
.map(Value::size_of_compound_entry)
.sum::<usize>()
}
}
impl<'a> Index<&'a str> for Blob {
type Output = Value;
fn index<'b>(&'b self, s: &'a str) -> &'b Value {
self.content.get(s).unwrap()
}
}
impl fmt::Display for Blob {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"TAG_Compound(\"{}\"): {} entry(ies)\n{{\n",
self.title,
self.content.len()
)?;
for (name, tag) in self.content.iter() {
write!(f, " {}(\"{}\"): ", tag.tag_name(), name)?;
tag.print(f, 2)?;
writeln!(f)?;
}
write!(f, "}}")
}
}
#[cfg(feature = "serde")]
use serde::{self, ser::SerializeMap};
#[cfg(feature = "serde")]
impl serde::Serialize for Blob {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let mut state = serializer.serialize_map(Some(self.content.len()))?;
for (k, v) in &self.content {
state.serialize_entry(&k, &v)?;
}
state.end()
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Blob {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let map: Map<String, Value> = serde::de::Deserialize::deserialize(deserializer)?;
Ok(Blob {
title: "".to_string(),
content: map,
})
}
}