I have a String x
which may or may not be gzip-compressed. Using the zlib
library, I want to try decompressing x
-- if it succeeds, the function shall return the compressed String. If not (i.e. x
is not gzip-compressed) I want to simply return x
.
As GZip.decompress
generates an error
if applied to a non-gzip string, I could use catch
or similar, but I'm specifically asking for a solution that uses the zlib
error handling mechanism.
How can I write a function, say decompressIfPossible :: ByteString -> ByteString
that has the previously described characteristics? I'd prefer a Either String ByteString
to represent either the error or the decompression result.
Note: This question intentionally does not show research effort, as it was immediately answered in a Q&A-style manner.
The function from zlib
you need to use here is called decompressWithErrors
. Its value is the recursive DecompressStream
data structure that you can fold to a ByteString
using v:fromDecompressStream
Here's a full example of how to write the function you asked for:
import Data.Either
import Codec.Compression.Zlib.Internal
import qualified Data.ByteString.Lazy.Char8 as LB
-- | Convert & unfold the custom DecompressStream
-- error format from zlib to a Either
decompressStreamToEither :: DecompressStream -> Either String LB.ByteString
decompressStreamToEither (StreamError _ errmsg) = Left errmsg
decompressStreamToEither stream@(StreamChunk _ _) = Right $ fromDecompressStream stream
decompressStreamToEither StreamEnd = Right $ ""
-- | Decompress with explicit error handling
safeDecompress :: LB.ByteString -> Either String LB.ByteString
safeDecompress bstr = decompressStreamToEither $ decompressWithErrors gzipOrZlibFormat defaultDecompressParams bstr
-- | Decompress gzip, if it fails, return uncompressed String
decompressIfPossible :: LB.ByteString -> LB.ByteString
decompressIfPossible bstr =
let conv (Left a) = bstr
conv (Right a) = a
in (conv . safeDecompress) bstr
Note that this example uses gzipOrZlibFormat
which automatically detects if the header is a zlib or a gzip header.