-- |
--
-- Module      : Raaz.Core.Encode
-- Description : Encoding and decoding values to formats.
-- Copyright   : (c) Piyush P Kurur, 2015
-- License     : Apache-2.0 OR BSD-3-Clause
-- Maintainer  : Piyush P Kurur <ppk@iitpkd.ac.in>
-- Stability   : experimental
--
module Raaz.Core.Encode
       ( -- * Encoding of binary data.
         -- $encodable$
         Encodable(..)
       , Format(..)
       , encode, decode, translate, unsafeDecode
       -- ** The base 16 encoding format
       , Base16
       , fromBase16, showBase16
       -- ** Other binary formats.
       , Base64
       ) where

import Raaz.Core.Prelude
import Raaz.Core.Encode.Internal
import Raaz.Core.Encode.Base16
import Raaz.Core.Encode.Base64

-- $encodable$
--
-- Often one wants to represent cryptographic hashes, secret keys or
-- just binary data into various enocoding formats like base64,
-- hexadecimal etc. This module gives a generic interface for all such
-- operations. There are two main classes that capture the essence of
-- encoding.
--
-- [`Format`:] The class of all types that are encoding formats to binary
--     data. They are all instances of `Show` and `Data.String.IsString` for
--     ease of printing and inclusion in source code.
--
-- [`Encodable`:] The class of all types that can be encoded /into/ binary.
--
-- The combinators `encode` and `decode` allows encoding any instance of `Encodable` to
-- any of the instances of `Format`.
--
-- == Sample code that makes use of Base16 encoding.
--
-- > theAnswer :: LE Word64
-- > theAnswer = 42
-- >
-- > main = do putStr "The answer to life, universe and everything is:"
-- >           print answserInBase16
-- >    where answerInBase16 :: Base16
-- >          answerInBase16 = encode theAnswer
-- >
-- > checkAnswer :: Base16 -> Bool
-- > checkAnswer = maybe False (==theAnswer) . decode
-- >
-- > checkAnswerBS :: ByteString -> Bool
-- > checkAnswerBS = checkAnswer . fromString
--
-- In the above example, @`LE` Word64@, which captures 64-bit unsigned
-- integers is an instance of Encode (but not Word64). The encode
-- combinator then converts in into the type Base16 that is an
-- instance of `Format`. The print then uses the `Show` instance of
-- Base16 to print it as a sequence of hexadecimal
-- characters. Similarly the decode combinator in @checkAnswer@
-- decodes a base16 before comparing with the answer.
--
-- == Liberal @IsString@ instances
--
-- Certain ascii printable formats like Base16 and Base64 have a more
-- liberal `IsString` instance: they typically allow the use of spaces
-- and newline in the input to the `fromString` function . This allows
-- a more readable representation of these types when using the
-- @OverloadedStrings@ extension. See the documentation of the
-- corresponding instance declarations to see what characters are
-- ignored. However, all `Show` instance of formats are strict in the
-- sense that they do not produce any such extraneous characters.

-- | Encode in a given format.
encode :: (Encodable a, Format fmt) => a -> fmt
encode :: a -> fmt
encode = ByteString -> fmt
forall fmt. Format fmt => ByteString -> fmt
encodeByteString (ByteString -> fmt) -> (a -> ByteString) -> a -> fmt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> ByteString
forall a. Encodable a => a -> ByteString
toByteString

-- | Decode from a given format. It results in Nothing if there is a
-- parse error.
decode :: (Format fmt, Encodable a) => fmt -> Maybe a
decode :: fmt -> Maybe a
decode = ByteString -> Maybe a
forall a. Encodable a => ByteString -> Maybe a
fromByteString (ByteString -> Maybe a) -> (fmt -> ByteString) -> fmt -> Maybe a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. fmt -> ByteString
forall fmt. Format fmt => fmt -> ByteString
decodeFormat

-- | The unsafe version of `decode`.
unsafeDecode :: (Format fmt, Encodable a) => fmt -> a
unsafeDecode :: fmt -> a
unsafeDecode = ByteString -> a
forall a. Encodable a => ByteString -> a
unsafeFromByteString (ByteString -> a) -> (fmt -> ByteString) -> fmt -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. fmt -> ByteString
forall fmt. Format fmt => fmt -> ByteString
decodeFormat

-- | Translate from one format to another.
translate :: (Format fmt1, Format fmt2) => fmt1 -> fmt2
translate :: fmt1 -> fmt2
translate = ByteString -> fmt2
forall fmt. Format fmt => ByteString -> fmt
encodeByteString (ByteString -> fmt2) -> (fmt1 -> ByteString) -> fmt1 -> fmt2
forall b c a. (b -> c) -> (a -> b) -> a -> c
. fmt1 -> ByteString
forall fmt. Format fmt => fmt -> ByteString
decodeFormat