kutil_transcoding/
reader.rs

1use super::encoding::*;
2
3use {
4    ::tokio::io::*,
5    async_compression::{tokio::bufread::*, *},
6    pin_project::*,
7    std::{io, pin::*, task::*},
8};
9
10//
11// TranscodingReader
12//
13
14/// [AsyncRead] wrapper that can encode, decode, or pass through.
15#[pin_project(project = Projected)]
16pub enum TranscodingReader<ReadT>
17where
18    ReadT: AsyncRead,
19{
20    /// Passthrough.
21    Passthrough(#[pin] ReadT),
22
23    /// Encode Brotli.
24    EncodeBrotli(#[pin] BrotliEncoder<BufReader<ReadT>>),
25
26    /// Decode Brotli.
27    DecodeBrotli(#[pin] BrotliDecoder<BufReader<ReadT>>),
28
29    /// Encode Deflate.
30    EncodeDeflate(#[pin] DeflateEncoder<BufReader<ReadT>>),
31
32    /// Decode Deflate.
33    DecodeDeflate(#[pin] DeflateDecoder<BufReader<ReadT>>),
34
35    /// Encode GZip.
36    EncodeGZip(#[pin] GzipEncoder<BufReader<ReadT>>),
37
38    /// Decode GZip.
39    DecodeGZip(#[pin] GzipDecoder<BufReader<ReadT>>),
40
41    /// Encode Zstandard.
42    EncodeZstandard(#[pin] ZstdEncoder<BufReader<ReadT>>),
43
44    /// Decode Zstandard.
45    DecodeZstandard(#[pin] ZstdDecoder<BufReader<ReadT>>),
46}
47
48impl<ReadT> AsyncRead for TranscodingReader<ReadT>
49where
50    ReadT: AsyncRead,
51{
52    fn poll_read(self: Pin<&mut Self>, context: &mut Context<'_>, buffer: &mut ReadBuf<'_>) -> Poll<io::Result<()>> {
53        match self.project() {
54            Projected::Passthrough(reader) => reader.poll_read(context, buffer),
55            Projected::EncodeBrotli(reader) => reader.poll_read(context, buffer),
56            Projected::DecodeBrotli(reader) => reader.poll_read(context, buffer),
57            Projected::EncodeGZip(reader) => reader.poll_read(context, buffer),
58            Projected::DecodeGZip(reader) => reader.poll_read(context, buffer),
59            Projected::EncodeDeflate(reader) => reader.poll_read(context, buffer),
60            Projected::DecodeDeflate(reader) => reader.poll_read(context, buffer),
61            Projected::EncodeZstandard(reader) => reader.poll_read(context, buffer),
62            Projected::DecodeZstandard(reader) => reader.poll_read(context, buffer),
63        }
64    }
65}
66
67//
68// IntoTranscodingReader
69//
70
71/// Into [TranscodingReader].
72pub trait IntoTranscodingReader<ReadT>
73where
74    ReadT: AsyncRead,
75{
76    /// As passthrough [TranscodingReader].
77    fn into_passthrough_reader(self) -> TranscodingReader<ReadT>;
78
79    /// As encoding [TranscodingReader].
80    fn into_encoding_reader(self, encoding: &Encoding, level: Level) -> TranscodingReader<ReadT>;
81
82    /// As decoding [TranscodingReader].
83    fn into_decoding_reader(self, encoding: &Encoding) -> TranscodingReader<ReadT>;
84}
85
86impl<ReadT> TranscodingReader<ReadT>
87where
88    ReadT: AsyncRead,
89{
90    /// Inner reader.
91    pub fn inner(&self) -> &ReadT {
92        match self {
93            Self::Passthrough(reader) => reader,
94            Self::EncodeBrotli(reader) => reader.get_ref().get_ref(),
95            Self::DecodeBrotli(reader) => reader.get_ref().get_ref(),
96            Self::EncodeDeflate(reader) => reader.get_ref().get_ref(),
97            Self::DecodeDeflate(reader) => reader.get_ref().get_ref(),
98            Self::EncodeGZip(reader) => reader.get_ref().get_ref(),
99            Self::DecodeGZip(reader) => reader.get_ref().get_ref(),
100            Self::EncodeZstandard(reader) => reader.get_ref().get_ref(),
101            Self::DecodeZstandard(reader) => reader.get_ref().get_ref(),
102        }
103    }
104}
105
106impl<ReadT> IntoTranscodingReader<ReadT> for ReadT
107where
108    ReadT: AsyncRead,
109{
110    fn into_passthrough_reader(self) -> TranscodingReader<ReadT> {
111        TranscodingReader::Passthrough(self)
112    }
113
114    fn into_encoding_reader(self, encoding: &Encoding, level: Level) -> TranscodingReader<ReadT> {
115        if *encoding == Encoding::Identity {
116            tracing::debug!("not encoding");
117        } else {
118            tracing::debug!("encoding to {}", encoding);
119        }
120
121        match encoding {
122            Encoding::Identity => self.into_passthrough_reader(),
123
124            Encoding::Brotli => {
125                TranscodingReader::EncodeBrotli(BrotliEncoder::with_quality(BufReader::new(self), level))
126            }
127
128            Encoding::Deflate => {
129                TranscodingReader::EncodeDeflate(DeflateEncoder::with_quality(BufReader::new(self), level))
130            }
131            Encoding::GZip => TranscodingReader::EncodeGZip(GzipEncoder::with_quality(BufReader::new(self), level)),
132
133            Encoding::Zstandard => {
134                TranscodingReader::EncodeZstandard(ZstdEncoder::with_quality(BufReader::new(self), level))
135            }
136        }
137    }
138
139    fn into_decoding_reader(self, encoding: &Encoding) -> TranscodingReader<ReadT> {
140        if *encoding == Encoding::Identity {
141            tracing::debug!("not decoding");
142        } else {
143            tracing::debug!("decoding from {}", encoding);
144        }
145
146        match encoding {
147            Encoding::Identity => self.into_passthrough_reader(),
148            Encoding::Brotli => TranscodingReader::DecodeBrotli(BrotliDecoder::new(BufReader::new(self))),
149            Encoding::Deflate => TranscodingReader::DecodeDeflate(DeflateDecoder::new(BufReader::new(self))),
150            Encoding::GZip => TranscodingReader::DecodeGZip(GzipDecoder::new(BufReader::new(self))),
151            Encoding::Zstandard => TranscodingReader::DecodeZstandard(ZstdDecoder::new(BufReader::new(self))),
152        }
153    }
154}