rustcompressiondropboxbrotli

switch from brotly-sys to Dropbox brotli


I used Brotly-sys but it seems unmaintened. So I use Brotli. I do an interface to compress and decompress (I use lzma and zstd too):

use std::io::{self, Write};

pub use brotli::writer::DecompressorWriter;
pub use brotli::enc::writer::CompressorWriter;

use super::Coder;

impl<W: Write> Coder<W> for DecompressorWriter<W> {
    fn get_mut(&mut self) -> &mut W {
        DecompressorWriter::get_mut(self)
    }

    fn finish(self) -> std::io::Result<W> {
        DecompressorWriter::flush(&mut self).map_err(|_| {
            io::Error::new(io::ErrorKind::Other, "brotli decoder failed to finalize stream")
        });
    }

    fn finish_boxed(self: Box<Self>) -> io::Result<W> {
        self.finish()
    }
}

impl<W: Write> Coder<W> for CompressorWriter<W> {
    fn get_mut(&mut self) -> &mut W {
        CompressorWriter::get_mut(self)
    }

    fn finish(self) -> std::io::Result<W> {
        self.flush()?;
        CompressorWriter::flush(&mut self).map_err(|_| {
            io::Error::new(io::ErrorKind::Other, "brotli encoder failed to finalize stream")
        })
    }

    fn finish_boxed(self: Box<Self>) -> io::Result<W> {
        self.finish()
    }
}

But get

error[E0308]: mismatched types
  --> lib/src/codecs/brotli.rs:13:24
   |
13 |     fn finish(self) -> std::io::Result<W> {
   |        ------          ^^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found `()`
   |        |
   |        implicitly returns `()` as its body has no tail or `return` expression
   |
   = note:   expected enum `std::result::Result<W, std::io::Error>`
           found unit type `()`

error[E0308]: mismatched types
  --> lib/src/codecs/brotli.rs:31:9
   |
24 |   impl<W: Write> Coder<W> for CompressorWriter<W> {
   |        - this type parameter
...
29 |       fn finish(self) -> std::io::Result<W> {
   |                          ------------------ expected `std::result::Result<W, std::io::Error>` because of return type
30 |           self.flush()?;
31 | /         CompressorWriter::flush(&mut self).map_err(|_| {
32 | |             io::Error::new(io::ErrorKind::Other, "brotli encoder failed to finalize stream")
33 | |         })
   | |__________^ expected type parameter `W`, found `()`
   |
   = note: expected enum `std::result::Result<W, _>`
              found enum `std::result::Result<(), _>`

But flush function doesn't implement Write trait.

Coder interface is here: https://github.com/Ludea/speedupdate-rs/blob/master/lib/src/codecs/mod.rs#L21

So how can I fix my issue ? (I can provide GH repo with this issue) I get this issue on MacOS / Win64 / Linux x86_64. Thanks


Solution

  • Your Coder trait is not compatible with the CompressorWriter struct. You might have to modify the Coder trait.

    I assume the flow of Coder is:

    Your trait forces the return value of get_mut() to be identical to the return value of finish(), which isn't the case for CompressorWriter.

    You don't need to introduce a new generic for this, though; you can simply return &mut dyn Write from your get_mut() function. This might require some minor tweaks with your other implementations of Coder, but should work.

    Minor side note: I renamed get_mut() to get_input_writer(), because it was clashing with CompressorWriter::get_mut(), making dealing with it much more annoying.

    use std::io::{self, Write};
    
    pub use brotli::enc::writer::CompressorWriter;
    pub use brotli::writer::DecompressorWriter;
    
    trait Coder<W> {
        fn get_input_writer(&mut self) -> &mut dyn Write;
        fn finish(self) -> io::Result<W>;
        fn finish_boxed(self: Box<Self>) -> io::Result<W>;
    }
    
    impl<W: Write> Coder<W> for CompressorWriter<W> {
        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }
    
        fn finish(mut self) -> io::Result<W> {
            self.flush().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::Other,
                    "brotli encoder failed to finalize stream",
                )
            })?;
            Ok(self.into_inner())
        }
    
        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }
    
    impl<W: Write> Coder<W> for DecompressorWriter<W> {
        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }
    
        fn finish(mut self) -> io::Result<W> {
            self.flush().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::Other,
                    "brotli decoder failed to finalize stream",
                )
            })?;
            self.into_inner().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::Other,
                    "brotli decoder failed to finalize stream",
                )
            })
        }
    
        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }
    
    fn main() {
        // Encode
        let mut coder: Box<dyn Coder<_>> = Box::new(CompressorWriter::with_params(
            Vec::new(),
            1024,
            &Default::default(),
        ));
    
        coder.get_input_writer().write_all(b"Hello world!").unwrap();
        let output = coder.finish_boxed().unwrap();
    
        println!("{:?}", output);
    
        // Decode
        let mut decoder: Box<dyn Coder<_>> = Box::new(DecompressorWriter::new(Vec::new(), 1024));
    
        decoder.get_input_writer().write_all(&output).unwrap();
        let output = decoder.finish_boxed().unwrap();
    
        println!("{:?}", String::from_utf8_lossy(&output));
    }
    
    [139, 5, 128, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 3]
    "Hello world!"
    

    While you are on it, I'd also change some other things, for convenience:

    use std::io::{self, Write};
    
    pub use brotli::enc::writer::CompressorWriter;
    pub use brotli::writer::DecompressorWriter;
    
    trait Coder {
        type Out;
    
        fn into_boxed_coder(self) -> Box<dyn Coder<Out = Self::Out>>
        where
            Self: Sized + 'static,
        {
            Box::new(self)
        }
    
        fn get_input_writer(&mut self) -> &mut dyn Write;
        fn finish(self) -> io::Result<Self::Out>;
        fn finish_boxed(self: Box<Self>) -> io::Result<Self::Out>;
    }
    
    impl<W: Write> Coder for CompressorWriter<W> {
        type Out = W;
    
        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }
    
        fn finish(mut self) -> io::Result<W> {
            self.flush().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::Other,
                    "brotli encoder failed to finalize stream",
                )
            })?;
            Ok(self.into_inner())
        }
    
        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }
    
    impl<W: Write> Coder for DecompressorWriter<W> {
        type Out = W;
    
        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }
    
        fn finish(mut self) -> io::Result<W> {
            self.flush().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::Other,
                    "brotli decoder failed to finalize stream",
                )
            })?;
            self.into_inner().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::Other,
                    "brotli decoder failed to finalize stream",
                )
            })
        }
    
        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }
    
    fn main() {
        // Encode
        let mut coder =
            CompressorWriter::with_params(Vec::new(), 1024, &Default::default()).into_boxed_coder();
    
        coder.get_input_writer().write_all(b"Hello world!").unwrap();
        let output = coder.finish_boxed().unwrap();
    
        println!("{:?}", output);
    
        // Decode
        let mut decoder = DecompressorWriter::new(Vec::new(), 1024).into_boxed_coder();
    
        decoder.get_input_writer().write_all(&output).unwrap();
        let output = decoder.finish_boxed().unwrap();
    
        println!("{:?}", String::from_utf8_lossy(&output));
    }
    
    [139, 5, 128, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 3]
    "Hello world!"
    

    Here is a more complete example with implementations for brotli, zstd and lzma:

    use std::{
        io::{self, Write},
        ops::Deref,
    };
    
    trait Coder {
        type Out;
    
        fn into_boxed_coder(self) -> Box<dyn Coder<Out = Self::Out>>
        where
            Self: Sized + 'static,
        {
            Box::new(self)
        }
    
        fn get_input_writer(&mut self) -> &mut dyn Write;
        fn finish(self) -> io::Result<Self::Out>;
        fn finish_boxed(self: Box<Self>) -> io::Result<Self::Out>;
    }
    
    mod zstd {
        use std::io::{self, Write};
        use zstd::stream::{write::Decoder, write::Encoder};
    
        impl<W: Write> super::Coder for Encoder<'static, W> {
            type Out = W;
    
            fn get_input_writer(&mut self) -> &mut dyn Write {
                self
            }
    
            fn finish(self) -> io::Result<W> {
                self.finish()
            }
    
            fn finish_boxed(self: Box<Self>) -> io::Result<W> {
                self.finish()
            }
        }
    
        impl<W: Write> super::Coder for Decoder<'static, W> {
            type Out = W;
    
            fn get_input_writer(&mut self) -> &mut dyn Write {
                self
            }
    
            fn finish(mut self) -> io::Result<W> {
                self.flush()?;
                Ok(self.into_inner())
            }
    
            fn finish_boxed(self: Box<Self>) -> io::Result<W> {
                self.finish()
            }
        }
    }
    
    mod brotli {
        use std::io::{self, Write};
    
        pub use brotli::enc::writer::CompressorWriter;
        pub use brotli::writer::DecompressorWriter;
    
        impl<W: Write> super::Coder for CompressorWriter<W> {
            type Out = W;
    
            fn get_input_writer(&mut self) -> &mut dyn Write {
                self
            }
    
            fn finish(mut self) -> io::Result<W> {
                self.flush().map_err(|_| {
                    io::Error::new(
                        io::ErrorKind::Other,
                        "brotli encoder failed to finalize stream",
                    )
                })?;
                Ok(self.into_inner())
            }
    
            fn finish_boxed(self: Box<Self>) -> io::Result<W> {
                self.finish()
            }
        }
    
        impl<W: Write> super::Coder for DecompressorWriter<W> {
            type Out = W;
    
            fn get_input_writer(&mut self) -> &mut dyn Write {
                self
            }
    
            fn finish(mut self) -> io::Result<W> {
                self.flush().map_err(|_| {
                    io::Error::new(
                        io::ErrorKind::Other,
                        "brotli decoder failed to finalize stream",
                    )
                })?;
                self.into_inner().map_err(|_| {
                    io::Error::new(
                        io::ErrorKind::Other,
                        "brotli decoder failed to finalize stream",
                    )
                })
            }
    
            fn finish_boxed(self: Box<Self>) -> io::Result<W> {
                self.finish()
            }
        }
    }
    
    mod lzma {
        use std::io::{self, Write};
    
        pub use xz2::write::{XzDecoder, XzEncoder};
    
        impl<W: Write> super::Coder for XzEncoder<W> {
            type Out = W;
    
            fn get_input_writer(&mut self) -> &mut dyn Write {
                self
            }
    
            fn finish(self) -> io::Result<W> {
                XzEncoder::finish(self)
            }
    
            fn finish_boxed(self: Box<Self>) -> io::Result<W> {
                self.finish()
            }
        }
    
        impl<W: Write> super::Coder for XzDecoder<W> {
            type Out = W;
    
            fn get_input_writer(&mut self) -> &mut dyn Write {
                self
            }
    
            fn finish(mut self) -> io::Result<W> {
                XzDecoder::finish(&mut self)
            }
    
            fn finish_boxed(self: Box<Self>) -> io::Result<W> {
                self.finish()
            }
        }
    }
    
    fn run_example<OutEncode, OutDecode>(
        mut encoder: Box<dyn Coder<Out = OutEncode>>,
        mut decoder: Box<dyn Coder<Out = OutDecode>>,
    ) where
        OutEncode: Write + std::fmt::Debug + Deref<Target = [u8]>,
        OutDecode: Write + Deref<Target = [u8]>,
    {
        // Encode
        encoder
            .get_input_writer()
            .write_all(b"Hello world!")
            .unwrap();
        let output = encoder.finish_boxed().unwrap();
    
        println!("{:?}", output);
    
        // Decode
        decoder.get_input_writer().write_all(&output).unwrap();
        let output = decoder.finish_boxed().unwrap();
    
        println!("{:?}", String::from_utf8_lossy(&output));
    }
    
    fn main() {
        println!("zstd:");
        run_example(
            ::zstd::stream::write::Encoder::new(Vec::new(), 0)
                .unwrap()
                .into_boxed_coder(),
            ::zstd::stream::write::Decoder::new(Vec::new())
                .unwrap()
                .into_boxed_coder(),
        );
        println!();
    
        println!("brotli:");
        run_example(
            ::brotli::enc::writer::CompressorWriter::with_params(Vec::new(), 1024, &Default::default())
                .into_boxed_coder(),
            ::brotli::writer::DecompressorWriter::new(Vec::new(), 1024).into_boxed_coder(),
        );
        println!();
    
        println!("lzma:");
        run_example(
            ::xz2::write::XzEncoder::new(Vec::new(), 5).into_boxed_coder(),
            ::xz2::write::XzDecoder::new(Vec::new()).into_boxed_coder(),
        );
        println!();
    }
    
    zstd:
    [40, 181, 47, 253, 0, 88, 97, 0, 0, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33]
    "Hello world!"
    
    brotli:
    [139, 5, 128, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 3]
    "Hello world!"
    
    lzma:
    [253, 55, 122, 88, 90, 0, 0, 4, 230, 214, 180, 70, 2, 0, 33, 1, 22, 0, 0, 0, 116, 47, 229, 163, 1, 0, 11, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 0, 10, 99, 214, 243, 246, 128, 91, 211, 0, 1, 36, 12, 166, 24, 216, 216, 31, 182, 243, 125, 1, 0, 0, 0, 0, 4, 89, 90]
    "Hello world!"