Skip to main content

async_proto/impls/
hematite_nbt.rs

1use {
2    std::{
3        io::prelude::*,
4        pin::Pin,
5    },
6    fallible_collections::FallibleVec as _,
7    tokio::io::{
8        AsyncRead,
9        AsyncReadExt as _,
10        AsyncWrite,
11        AsyncWriteExt as _,
12    },
13    async_proto_derive::impl_protocol_for,
14    crate::{
15        ErrorContext,
16        LengthPrefixed,
17        Protocol,
18        ReadError,
19        ReadErrorKind,
20        WriteError,
21        WriteErrorKind,
22    },
23};
24
25/// An [`nbt::Blob`] is Gzip-compressed and prefixed with the length of the blob after compression as a [`u64`].
26#[cfg_attr(docsrs, doc(cfg(feature = "hematite-nbt")))]
27impl Protocol for nbt::Blob {
28    fn read<'a, R: AsyncRead + Unpin + Send + 'a>(stream: &'a mut R) -> Pin<Box<dyn Future<Output = Result<Self, ReadError>> + Send + 'a>> {
29        Self::read_length_prefixed(stream, u64::MAX)
30    }
31
32    fn write<'a, W: AsyncWrite + Unpin + Send + 'a>(&'a self, sink: &'a mut W) -> Pin<Box<dyn Future<Output = Result<(), WriteError>> + Send + 'a>> {
33        self.write_length_prefixed(sink, u64::MAX)
34    }
35
36    fn read_sync(stream: &mut impl Read) -> Result<Self, ReadError> {
37        Self::read_length_prefixed_sync(stream, u64::MAX)
38    }
39
40    fn write_sync(&self, sink: &mut impl Write) -> Result<(), WriteError> {
41        self.write_length_prefixed_sync(sink, u64::MAX)
42    }
43}
44
45#[cfg_attr(docsrs, doc(cfg(feature = "hematite-nbt")))]
46impl LengthPrefixed for nbt::Blob {
47    fn read_length_prefixed<'a, R: AsyncRead + Unpin + Send + 'a>(stream: &'a mut R, max_len: u64) -> Pin<Box<dyn Future<Output = Result<Self, ReadError>> + Send + 'a>> {
48        Box::pin(async move {
49            let len = super::read_len(stream, max_len, || ErrorContext::BuiltIn { for_type: "nbt::Blob" }).await?;
50            let mut buf = Vec::default();
51            buf.try_resize(len, 0).map_err(|e| ReadError {
52                context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
53                kind: e.into(),
54            })?;
55            stream.read_exact(&mut buf).await.map_err(|e| ReadError {
56                context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
57                kind: e.into(),
58            })?;
59            Self::from_gzip_reader(&mut &*buf).map_err(|e| ReadError {
60                context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
61                kind: ReadErrorKind::Custom(e.to_string()),
62            })
63        })
64    }
65
66    fn write_length_prefixed<'a, W: AsyncWrite + Unpin + Send + 'a>(&'a self, sink: &'a mut W, max_len: u64) -> Pin<Box<dyn Future<Output = Result<(), WriteError>> + Send + 'a>> {
67        Box::pin(async move {
68            let mut buf = Vec::default();
69            self.to_gzip_writer(&mut buf).map_err(|e| WriteError {
70                context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
71                kind: WriteErrorKind::Custom(e.to_string()),
72            })?;
73            super::write_len(sink, buf.len(), max_len, || ErrorContext::BuiltIn { for_type: "nbt::Blob" }).await?;
74            sink.write_all(&buf).await.map_err(|e| WriteError {
75                context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
76                kind: e.into(),
77            })?;
78            Ok(())
79        })
80    }
81
82    fn read_length_prefixed_sync(stream: &mut impl Read, max_len: u64) -> Result<Self, ReadError> {
83        let len = super::read_len_sync(stream, max_len, || ErrorContext::BuiltIn { for_type: "nbt::Blob" })?;
84        let mut buf = Vec::default();
85        buf.try_resize(len, 0).map_err(|e| ReadError {
86            context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
87            kind: e.into(),
88        })?;
89        stream.read_exact(&mut buf).map_err(|e| ReadError {
90            context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
91            kind: e.into(),
92        })?;
93        Self::from_gzip_reader(&mut &*buf).map_err(|e| ReadError {
94            context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
95            kind: ReadErrorKind::Custom(e.to_string()),
96        })
97    }
98
99    fn write_length_prefixed_sync(&self, sink: &mut impl Write, max_len: u64) -> Result<(), WriteError> {
100        let mut buf = Vec::default();
101        self.to_gzip_writer(&mut buf).map_err(|e| WriteError {
102            context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
103            kind: WriteErrorKind::Custom(e.to_string()),
104        })?;
105        super::write_len_sync(sink, buf.len(), max_len, || ErrorContext::BuiltIn { for_type: "nbt::Blob" })?;
106        sink.write_all(&buf).map_err(|e| WriteError {
107            context: ErrorContext::BuiltIn { for_type: "nbt::Blob" },
108            kind: e.into(),
109        })?;
110        Ok(())
111    }
112}
113
114#[derive(Protocol)]
115#[async_proto(internal)]
116struct ValueProxy(nbt::Blob);
117
118impl TryFrom<ValueProxy> for nbt::Value {
119    type Error = ReadErrorKind;
120
121    fn try_from(ValueProxy(blob): ValueProxy) -> Result<Self, Self::Error> {
122        Ok(blob.get("").ok_or_else(|| ReadErrorKind::Custom(format!("NBT blob missing empty-string entry")))?.clone())
123    }
124}
125
126impl TryFrom<nbt::Value> for ValueProxy {
127    type Error = WriteErrorKind;
128
129    fn try_from(value: nbt::Value) -> Result<Self, Self::Error> {
130        let mut blob = nbt::Blob::default();
131        blob.insert("", value).map_err(|e| WriteErrorKind::Custom(e.to_string()))?;
132        Ok(Self(blob))
133    }
134}
135
136impl_protocol_for! {
137    #[async_proto(attr(cfg_attr(docsrs, doc(cfg(feature = "hematite-nbt")))))]
138    #[async_proto(attr(doc = "An [`nbt::Value`] is represented as an [`nbt::Blob`] with no name and a single entry with no name containing the original value."))]
139    #[async_proto(via = ValueProxy, clone)]
140    type nbt::Value;
141}