{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}

{-# OPTIONS_GHC -Wno-deprecations #-}

-- | type-encoding overview examples. 
--
-- This library is concerned with 3 main operations done on strings:
-- __encoding__, __decoding__, and __recovery__.  Examples in this module cover all
-- of these base cases.
--
-- This module uses encoding instances found in 
--
-- * "Data.TypedEncoding.Instances.Enc.Base64"
-- * "Data.TypedEncoding.Instances.Restriction.ASCII"
-- * "Examples.TypedEncoding.Instances.Do.Sample"
--

module Examples.TypedEncoding.Overview where

import           Data.TypedEncoding
import           Data.TypedEncoding.Instances.Enc.Base64 ()
import           Data.TypedEncoding.Instances.Enc.Warn.Base64 ()
import           Data.TypedEncoding.Instances.Restriction.ASCII ()
import           Examples.TypedEncoding.Instances.Do.Sample
import           Examples.TypedEncoding.Util (HasA (..))
 
import qualified Data.ByteString as B
import qualified Data.Text as T


-- $setup
-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications
-- >>> import Data.Functor.Identity
--
-- This module contains some ghci friendly values to play with.
--
-- Each value is documented in a doctest style by including an equivalent ghci ready expression.
-- These documents generate a test suite for this library as well.

-- * Basics

-- | "Hello World" encoded as Base64
--
-- >>> helloB64 
-- UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="
--
-- >>> displ helloB64
-- "Enc '[enc-B64] () (ByteString SGVsbG8gV29ybGQ=)"
-- 
-- >>> encodeAll . toEncoding () $ "Hello World" :: Enc '["enc-B64"] () B.ByteString
-- UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="
helloB64 :: Enc '["enc-B64"] () B.ByteString
helloB64 :: Enc @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
helloB64 = Enc @[Symbol] ('[] @Symbol) () ByteString
-> Enc
     @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
forall (nms :: [Symbol]) c str.
EncodeAll Identity nms nms c str =>
Enc @[Symbol] ('[] @Symbol) c str -> Enc @[Symbol] nms c str
encodeAll (Enc @[Symbol] ('[] @Symbol) () ByteString
 -> Enc
      @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString)
-> (ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString)
-> ByteString
-> Enc
     @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (ByteString
 -> Enc
      @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString)
-> ByteString
-> Enc
     @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
"Hello World"

-- | "Hello World" double-Base64 encoded.
-- Notice the same code used as in single encoding, the game is played at type level.
--
-- >>> encodeAll . toEncoding () $ "Hello World" :: Enc '["enc-B64","enc-B64"] () B.ByteString  
-- UnsafeMkEnc Proxy () "U0dWc2JHOGdWMjl5YkdRPQ=="
--
-- >>> displ helloB64B64
-- "Enc '[enc-B64,enc-B64] () (ByteString U0dWc2JHOGdWMjl5YkdRPQ==)"
--
-- An alternative version of the above code is this:
--
-- >>> fmap displ . runEncodings' @'["enc-B64","enc-B64"] @'["enc-B64","enc-B64"] @Identity encodings . toEncoding () $ ("Hello World" :: B.ByteString)
-- Identity "Enc '[enc-B64,enc-B64] () (ByteString U0dWc2JHOGdWMjl5YkdRPQ==)"
--
-- This is how /typed-encoding/ works, the "Data.TypedEncoding.Common.Class.Encode.EncodeAll"
-- constraint can be used to get access to list to encodings required by the symbol annotation. 
-- 'runEncodings'' executes all the necessary transformations.
--
-- Similar story is true for /decoding/ and /validation/. In these examples we will use shortcut combinators.
helloB64B64 :: Enc '["enc-B64","enc-B64"] () B.ByteString
helloB64B64 :: Enc
  @[Symbol]
  ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
  ()
  ByteString
helloB64B64 = Enc @[Symbol] ('[] @Symbol) () ByteString
-> Enc
     @[Symbol]
     ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
     ()
     ByteString
forall (nms :: [Symbol]) c str.
EncodeAll Identity nms nms c str =>
Enc @[Symbol] ('[] @Symbol) c str -> Enc @[Symbol] nms c str
encodeAll (Enc @[Symbol] ('[] @Symbol) () ByteString
 -> Enc
      @[Symbol]
      ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
      ()
      ByteString)
-> (ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString)
-> ByteString
-> Enc
     @[Symbol]
     ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
     ()
     ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (ByteString
 -> Enc
      @[Symbol]
      ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
      ()
      ByteString)
-> ByteString
-> Enc
     @[Symbol]
     ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
     ()
     ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
"Hello World"

-- | Previous text decoded from Base64
--
-- >>> fromEncoding . decodeAll $ helloB64 
-- "Hello World"
helloB64Decoded :: B.ByteString
helloB64Decoded :: ByteString
helloB64Decoded = Enc @[Symbol] ('[] @Symbol) () ByteString -> ByteString
forall k conf str. Enc @[k] ('[] @k) conf str -> str
fromEncoding (Enc @[Symbol] ('[] @Symbol) () ByteString -> ByteString)
-> (Enc
      @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
    -> Enc @[Symbol] ('[] @Symbol) () ByteString)
-> Enc
     @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Enc @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
-> Enc @[Symbol] ('[] @Symbol) () ByteString
forall (nms :: [Symbol]) c str.
DecodeAll Identity nms nms c str =>
Enc @[Symbol] nms c str -> Enc @[Symbol] ('[] @Symbol) c str
decodeAll (Enc @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
 -> ByteString)
-> Enc
     @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
-> ByteString
forall a b. (a -> b) -> a -> b
$ Enc @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
helloB64

-- | 'recreateFAll' allows for recovering data at program boundaries (for example, when parsing JSON input).
-- It makes sure that the content satisfies specified encodings.
-- 
-- >>> recreateFAll . toEncoding () $ "SGVsbG8gV29ybGQ=" :: Either RecreateEx (Enc '["enc-B64"] () B.ByteString)
-- Right (UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=")
--
-- >>> recreateFAll . toEncoding () $ "SGVsbG8gV29ybGQ" :: Either RecreateEx (Enc '["enc-B64"] () B.ByteString)
-- Left (RecreateEx "enc-B64" ("Base64-encoded bytestring is unpadded or has invalid padding"))
--
-- The above example start by placing payload in zero-encoded @Enc '[] ()@ type and then apply 'recreateFAll'
-- this is a good way to recreate encoded type if encoding is known. 
--
-- If is it not, 'UncheckedEnc' type can be used. 
--
-- (See 'Examples.TypedEncoding.ToEncString' for better example).
-- 
-- This module is concerned only with the first approach. 
--
-- >>> let unchecked = toUncheckedEnc ["enc-B64"] () ("SGVsbG8gV29ybGQ=" :: T.Text)
-- >>> check @'["enc-B64"] @(Either RecreateEx) unchecked
-- Just (Right (UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="))
helloB64Recovered :: Either RecreateEx (Enc '["enc-B64"] () B.ByteString)
helloB64Recovered :: Either
  RecreateEx
  (Enc
     @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString)
helloB64Recovered = Enc @[Symbol] ('[] @Symbol) () ByteString
-> Either
     RecreateEx
     (Enc
        @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString)
forall (nms :: [Symbol]) (f :: * -> *) c str.
(Monad f, ValidateAll f nms nms c str) =>
Enc @[Symbol] ('[] @Symbol) c str -> f (Enc @[Symbol] nms c str)
recreateFAll (Enc @[Symbol] ('[] @Symbol) () ByteString
 -> Either
      RecreateEx
      (Enc
         @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString))
-> (ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString)
-> ByteString
-> Either
     RecreateEx
     (Enc
        @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (ByteString
 -> Either
      RecreateEx
      (Enc
         @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString))
-> ByteString
-> Either
     RecreateEx
     (Enc
        @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString)
forall a b. (a -> b) -> a -> b
$ ByteString
"SGVsbG8gV29ybGQ="


-- | Double Base64 encoded "Hello World" with one layer of encoding removed
--
-- >>> decodePart @'["enc-B64"] $ helloB64B64 :: Enc '["enc-B64"] () B.ByteString
-- UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="
--
-- >>> helloB64B64PartDecode == helloB64
-- True
--
-- @decodePart@ is a convenience function that simply replies decoding 'above' first "enc-B64"
--
-- >>> above @'["enc-B64"] @'["enc-B64"] @'[] decodeAll $ helloB64B64 
-- UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ="
helloB64B64PartDecode :: Enc '["enc-B64"] () B.ByteString
helloB64B64PartDecode :: Enc @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
helloB64B64PartDecode = Enc
  @[Symbol]
  (Append
     @Symbol
     ((':) @Symbol "enc-B64" ('[] @Symbol))
     ((':) @Symbol "enc-B64" ('[] @Symbol)))
  ()
  ByteString
-> Enc
     @[Symbol] ((':) @Symbol "enc-B64" ('[] @Symbol)) () ByteString
forall (xs :: [Symbol]) (xsf :: [Symbol]) c str.
DecodeAll Identity xs xs c str =>
Enc @[Symbol] (Append @Symbol xs xsf) c str
-> Enc @[Symbol] xsf c str
decodePart @'["enc-B64"] Enc
  @[Symbol]
  ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
  ()
  ByteString
Enc
  @[Symbol]
  (Append
     @Symbol
     ((':) @Symbol "enc-B64" ('[] @Symbol))
     ((':) @Symbol "enc-B64" ('[] @Symbol)))
  ()
  ByteString
helloB64B64

-- | 'helloB64B64' all the way to 'B.ByteString'
--
-- Notice a similar polymorphism in decoding.
--
-- >>> fromEncoding . decodeAll $ helloB64B64 :: B.ByteString 
-- "Hello World"
-- 
-- We can also decode all the parts: 
--
-- >>> fromEncoding . decodePart @'["enc-B64","enc-B64"] $ helloB64B64
-- "Hello World"
helloB64B64Decoded :: B.ByteString
helloB64B64Decoded :: ByteString
helloB64B64Decoded = Enc @[Symbol] ('[] @Symbol) () ByteString -> ByteString
forall k conf str. Enc @[k] ('[] @k) conf str -> str
fromEncoding (Enc @[Symbol] ('[] @Symbol) () ByteString -> ByteString)
-> (Enc
      @[Symbol]
      ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
      ()
      ByteString
    -> Enc @[Symbol] ('[] @Symbol) () ByteString)
-> Enc
     @[Symbol]
     ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
     ()
     ByteString
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Enc
  @[Symbol]
  ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
  ()
  ByteString
-> Enc @[Symbol] ('[] @Symbol) () ByteString
forall (nms :: [Symbol]) c str.
DecodeAll Identity nms nms c str =>
Enc @[Symbol] nms c str -> Enc @[Symbol] ('[] @Symbol) c str
decodeAll (Enc
   @[Symbol]
   ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
   ()
   ByteString
 -> ByteString)
-> Enc
     @[Symbol]
     ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
     ()
     ByteString
-> ByteString
forall a b. (a -> b) -> a -> b
$ Enc
  @[Symbol]
  ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
  ()
  ByteString
helloB64B64

-- | what happens when we try to recover encoded once text to @Enc '["enc-B64", "enc-B64"]@. 
--
-- Again, notice the same expression is used as in previous recovery. 
--
-- >>> recreateFAll . toEncoding () $ "SGVsbG8gV29ybGQ=" :: Either RecreateEx (Enc '["enc-B64", "enc-B64"] () B.ByteString)
-- Left (RecreateEx "enc-B64" ("Base64-encoded bytestring is unpadded or has invalid padding"))
helloB64B64RecoveredErr :: Either RecreateEx (Enc '["enc-B64", "enc-B64"] () B.ByteString)
helloB64B64RecoveredErr :: Either
  RecreateEx
  (Enc
     @[Symbol]
     ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
     ()
     ByteString)
helloB64B64RecoveredErr = Enc @[Symbol] ('[] @Symbol) () ByteString
-> Either
     RecreateEx
     (Enc
        @[Symbol]
        ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
        ()
        ByteString)
forall (nms :: [Symbol]) (f :: * -> *) c str.
(Monad f, ValidateAll f nms nms c str) =>
Enc @[Symbol] ('[] @Symbol) c str -> f (Enc @[Symbol] nms c str)
recreateFAll (Enc @[Symbol] ('[] @Symbol) () ByteString
 -> Either
      RecreateEx
      (Enc
         @[Symbol]
         ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
         ()
         ByteString))
-> (ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString)
-> ByteString
-> Either
     RecreateEx
     (Enc
        @[Symbol]
        ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
        ()
        ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (ByteString
 -> Either
      RecreateEx
      (Enc
         @[Symbol]
         ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
         ()
         ByteString))
-> ByteString
-> Either
     RecreateEx
     (Enc
        @[Symbol]
        ((':) @Symbol "enc-B64" ((':) @Symbol "enc-B64" ('[] @Symbol)))
        ()
        ByteString)
forall a b. (a -> b) -> a -> b
$ ByteString
"SGVsbG8gV29ybGQ="



-- * "do-" Encodings

-- |
-- "do-UPPER" (from 'Examples.TypedEncoding.Instances.Do.Sample' module) encoding applied to "Hello World"
--
-- Notice a namespace thing going on, "enc-" is encoding, "do-" is some transformation. 
-- These are typically not reversible, some could be recoverable.
--  
-- The same code is used as in "enc-" examples to encode (now transform).
--
-- >>> encodeAll . toEncoding () $ "Hello World" :: Enc '["do-UPPER"] () T.Text
-- UnsafeMkEnc Proxy () "HELLO WORLD"
helloUPP :: Enc '["do-UPPER"] () T.Text
helloUPP :: Enc @[Symbol] ((':) @Symbol "do-UPPER" ('[] @Symbol)) () Text
helloUPP = Enc @[Symbol] ('[] @Symbol) () Text
-> Enc @[Symbol] ((':) @Symbol "do-UPPER" ('[] @Symbol)) () Text
forall (nms :: [Symbol]) c str.
EncodeAll Identity nms nms c str =>
Enc @[Symbol] ('[] @Symbol) c str -> Enc @[Symbol] nms c str
encodeAll (Enc @[Symbol] ('[] @Symbol) () Text
 -> Enc @[Symbol] ((':) @Symbol "do-UPPER" ('[] @Symbol)) () Text)
-> (Text -> Enc @[Symbol] ('[] @Symbol) () Text)
-> Text
-> Enc @[Symbol] ((':) @Symbol "do-UPPER" ('[] @Symbol)) () Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> Text -> Enc @[Symbol] ('[] @Symbol) () Text
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (Text
 -> Enc @[Symbol] ((':) @Symbol "do-UPPER" ('[] @Symbol)) () Text)
-> Text
-> Enc @[Symbol] ((':) @Symbol "do-UPPER" ('[] @Symbol)) () Text
forall a b. (a -> b) -> a -> b
$ Text
"Hello World"

-- | Sample compound transformation 
-- 
-- >>> encodeAll . toEncoding () $ "HeLLo world" :: Enc '["do-reverse", "do-Title"] () T.Text
-- UnsafeMkEnc Proxy () "dlroW olleH" 
helloTitleRev :: Enc '["do-reverse", "do-Title"] () T.Text
helloTitleRev :: Enc
  @[Symbol]
  ((':) @Symbol "do-reverse" ((':) @Symbol "do-Title" ('[] @Symbol)))
  ()
  Text
helloTitleRev = Enc @[Symbol] ('[] @Symbol) () Text
-> Enc
     @[Symbol]
     ((':) @Symbol "do-reverse" ((':) @Symbol "do-Title" ('[] @Symbol)))
     ()
     Text
forall (nms :: [Symbol]) c str.
EncodeAll Identity nms nms c str =>
Enc @[Symbol] ('[] @Symbol) c str -> Enc @[Symbol] nms c str
encodeAll (Enc @[Symbol] ('[] @Symbol) () Text
 -> Enc
      @[Symbol]
      ((':) @Symbol "do-reverse" ((':) @Symbol "do-Title" ('[] @Symbol)))
      ()
      Text)
-> (Text -> Enc @[Symbol] ('[] @Symbol) () Text)
-> Text
-> Enc
     @[Symbol]
     ((':) @Symbol "do-reverse" ((':) @Symbol "do-Title" ('[] @Symbol)))
     ()
     Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> Text -> Enc @[Symbol] ('[] @Symbol) () Text
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (Text
 -> Enc
      @[Symbol]
      ((':) @Symbol "do-reverse" ((':) @Symbol "do-Title" ('[] @Symbol)))
      ()
      Text)
-> Text
-> Enc
     @[Symbol]
     ((':) @Symbol "do-reverse" ((':) @Symbol "do-Title" ('[] @Symbol)))
     ()
     Text
forall a b. (a -> b) -> a -> b
$ Text
"HeLLo world"



-- * Configuration

-- | Example configuration
newtype Config = Config {
    Config -> SizeLimit
sizeLimit :: SizeLimit
  } deriving (Int -> Config -> ShowS
[Config] -> ShowS
Config -> String
(Int -> Config -> ShowS)
-> (Config -> String) -> ([Config] -> ShowS) -> Show Config
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Config] -> ShowS
$cshowList :: [Config] -> ShowS
show :: Config -> String
$cshow :: Config -> String
showsPrec :: Int -> Config -> ShowS
$cshowsPrec :: Int -> Config -> ShowS
Show)
exampleConf :: Config
exampleConf = SizeLimit -> Config
Config (Int -> SizeLimit
SizeLimit Int
8) 

instance HasA SizeLimit Config where
   has :: Config -> SizeLimit
has = Config -> SizeLimit
sizeLimit  

-- | @helloTitle'@ is needed in following examples
--
helloTitle :: Enc '["do-Title"] Config T.Text
helloTitle :: Enc @[Symbol] ((':) @Symbol "do-Title" ('[] @Symbol)) Config Text
helloTitle = Enc @[Symbol] ('[] @Symbol) Config Text
-> Enc
     @[Symbol] ((':) @Symbol "do-Title" ('[] @Symbol)) Config Text
forall (nms :: [Symbol]) c str.
EncodeAll Identity nms nms c str =>
Enc @[Symbol] ('[] @Symbol) c str -> Enc @[Symbol] nms c str
encodeAll (Enc @[Symbol] ('[] @Symbol) Config Text
 -> Enc
      @[Symbol] ((':) @Symbol "do-Title" ('[] @Symbol)) Config Text)
-> (Text -> Enc @[Symbol] ('[] @Symbol) Config Text)
-> Text
-> Enc
     @[Symbol] ((':) @Symbol "do-Title" ('[] @Symbol)) Config Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Config -> Text -> Enc @[Symbol] ('[] @Symbol) Config Text
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding Config
exampleConf (Text
 -> Enc
      @[Symbol] ((':) @Symbol "do-Title" ('[] @Symbol)) Config Text)
-> Text
-> Enc
     @[Symbol] ((':) @Symbol "do-Title" ('[] @Symbol)) Config Text
forall a b. (a -> b) -> a -> b
$ Text
"hello wOrld"

-- | Configuration can be used to impact the encoding process.
--
-- So far we had used @()@ as configuration of all encodings.
-- But since both "do-reverse", "do-Title" are polymorphic in 
-- configuration we can also do this:
--
-- >>> encodeAll . toEncoding exampleConf $ "HeLLo world" :: Enc '["do-reverse", "do-Title"] Config T.Text
-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW olleH"
--
-- >>> encodeAll . toEncoding exampleConf $ "HeLlo world" :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config T.Text
-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol"
--
-- Instead, encode previously defined 'helloTitle' by reversing it and adding size limit
--
-- >>> encodePart @'["do-size-limit", "do-reverse"] helloTitle :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config T.Text
-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol"
--
-- @encodePart@ is simply encodeAll played above "do-Title" encoding:
--
-- >>> above @'["do-Title"] @'[] @'["do-size-limit", "do-reverse"] encodeAll helloTitle
-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol"
helloRevLimit :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config T.Text
helloRevLimit :: Enc
  @[Symbol]
  ((':)
     @Symbol
     "do-size-limit"
     ((':)
        @Symbol "do-reverse" ((':) @Symbol "do-Title" ('[] @Symbol))))
  Config
  Text
helloRevLimit = Enc @[Symbol] ((':) @Symbol "do-Title" ('[] @Symbol)) Config Text
-> Enc
     @[Symbol]
     (Append
        @Symbol
        ((':)
           @Symbol "do-size-limit" ((':) @Symbol "do-reverse" ('[] @Symbol)))
        ((':) @Symbol "do-Title" ('[] @Symbol)))
     Config
     Text
forall (xs :: [Symbol]) (xsf :: [Symbol]) c str.
EncodeAll Identity xs xs c str =>
Enc @[Symbol] xsf c str
-> Enc @[Symbol] (Append @Symbol xs xsf) c str
encodePart @'["do-size-limit", "do-reverse"] Enc @[Symbol] ((':) @Symbol "do-Title" ('[] @Symbol)) Config Text
helloTitle

-- >>> encodeAll . toEncoding exampleConf $ "HeLlo world" :: Enc '["enc-B64", "do-size-limit"] Config B.ByteString
-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "SGVMbG8gd28="
helloLimitB64 :: Enc '["enc-B64", "do-size-limit"] Config B.ByteString
helloLimitB64 :: Enc
  @[Symbol]
  ((':)
     @Symbol "enc-B64" ((':) @Symbol "do-size-limit" ('[] @Symbol)))
  Config
  ByteString
helloLimitB64 = Enc @[Symbol] ('[] @Symbol) Config ByteString
-> Enc
     @[Symbol]
     ((':)
        @Symbol "enc-B64" ((':) @Symbol "do-size-limit" ('[] @Symbol)))
     Config
     ByteString
forall (nms :: [Symbol]) c str.
EncodeAll Identity nms nms c str =>
Enc @[Symbol] ('[] @Symbol) c str -> Enc @[Symbol] nms c str
encodeAll (Enc @[Symbol] ('[] @Symbol) Config ByteString
 -> Enc
      @[Symbol]
      ((':)
         @Symbol "enc-B64" ((':) @Symbol "do-size-limit" ('[] @Symbol)))
      Config
      ByteString)
-> (ByteString -> Enc @[Symbol] ('[] @Symbol) Config ByteString)
-> ByteString
-> Enc
     @[Symbol]
     ((':)
        @Symbol "enc-B64" ((':) @Symbol "do-size-limit" ('[] @Symbol)))
     Config
     ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Config
-> ByteString -> Enc @[Symbol] ('[] @Symbol) Config ByteString
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding Config
exampleConf (ByteString
 -> Enc
      @[Symbol]
      ((':)
         @Symbol "enc-B64" ((':) @Symbol "do-size-limit" ('[] @Symbol)))
      Config
      ByteString)
-> ByteString
-> Enc
     @[Symbol]
     ((':)
        @Symbol "enc-B64" ((':) @Symbol "do-size-limit" ('[] @Symbol)))
     Config
     ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
"HeLlo world"

-- | ... and we unwrap the B64 part only
-- 
-- >>> decodePart @'["enc-B64"] $ helloLimitB64
-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "HeLlo wo"
helloRevLimitParDec :: Enc '["do-size-limit"] Config B.ByteString
helloRevLimitParDec :: Enc
  @[Symbol]
  ((':) @Symbol "do-size-limit" ('[] @Symbol))
  Config
  ByteString
helloRevLimitParDec =  Enc
  @[Symbol]
  (Append
     @Symbol
     ((':) @Symbol "enc-B64" ('[] @Symbol))
     ((':) @Symbol "do-size-limit" ('[] @Symbol)))
  Config
  ByteString
-> Enc
     @[Symbol]
     ((':) @Symbol "do-size-limit" ('[] @Symbol))
     Config
     ByteString
forall (xs :: [Symbol]) (xsf :: [Symbol]) c str.
DecodeAll Identity xs xs c str =>
Enc @[Symbol] (Append @Symbol xs xsf) c str
-> Enc @[Symbol] xsf c str
decodePart @'["enc-B64"] Enc
  @[Symbol]
  ((':)
     @Symbol "enc-B64" ((':) @Symbol "do-size-limit" ('[] @Symbol)))
  Config
  ByteString
Enc
  @[Symbol]
  (Append
     @Symbol
     ((':) @Symbol "enc-B64" ('[] @Symbol))
     ((':) @Symbol "do-size-limit" ('[] @Symbol)))
  Config
  ByteString
helloLimitB64




-- * "r-" encodings section

-- | ASCII char set
-- ByteStrings are sequences of Bytes ('Data.Word.Word8'). The type
-- is very permissive, it may contain binary data such as jpeg picture.
--
-- "r-ASCII" encoding acts as partial identity function
-- it does not change any bytes in bytestring but it fails if a byte
-- is outside of ASCII range (in @Either@ monad).
--
-- Note naming thing: "r-" is partial identity ("r-" is from restriction).
--
-- >>>   _runEncodings encodings . toEncoding () $ "HeLlo world" :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString) 
-- Right (UnsafeMkEnc Proxy () "HeLlo world")
--
-- or equivalently
--
-- >>>  encodeFAll . toEncoding () $ "HeLlo world" :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString) 
-- Right (UnsafeMkEnc Proxy () "HeLlo world")
helloAscii :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString)
helloAscii :: Either
  EncodeEx
  (Enc
     @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () ByteString)
helloAscii = Enc @[Symbol] ('[] @Symbol) () ByteString
-> Either
     EncodeEx
     (Enc
        @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () ByteString)
forall (nms :: [Symbol]) (f :: * -> *) c str.
(Monad f, EncodeAll f nms nms c str) =>
Enc @[Symbol] ('[] @Symbol) c str -> f (Enc @[Symbol] nms c str)
encodeFAll (Enc @[Symbol] ('[] @Symbol) () ByteString
 -> Either
      EncodeEx
      (Enc
         @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () ByteString))
-> (ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString)
-> ByteString
-> Either
     EncodeEx
     (Enc
        @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (ByteString
 -> Either
      EncodeEx
      (Enc
         @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () ByteString))
-> ByteString
-> Either
     EncodeEx
     (Enc
        @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () ByteString)
forall a b. (a -> b) -> a -> b
$ ByteString
"HeLlo world" 

-- | Arguably the type we used for helloB64 was too permissive.
-- a better version is here:
--
-- >>> encodeFAll . toEncoding () $ "Hello World" :: Either EncodeEx (Enc '["enc-B64", "r-ASCII"] () B.ByteString)
-- Right (UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=") 
helloAsciiB64 :: Either EncodeEx (Enc '["enc-B64", "r-ASCII"] () B.ByteString)
helloAsciiB64 :: Either
  EncodeEx
  (Enc
     @[Symbol]
     ((':) @Symbol "enc-B64" ((':) @Symbol "r-ASCII" ('[] @Symbol)))
     ()
     ByteString)
helloAsciiB64 = Enc @[Symbol] ('[] @Symbol) () ByteString
-> Either
     EncodeEx
     (Enc
        @[Symbol]
        ((':) @Symbol "enc-B64" ((':) @Symbol "r-ASCII" ('[] @Symbol)))
        ()
        ByteString)
forall (nms :: [Symbol]) (f :: * -> *) c str.
(Monad f, EncodeAll f nms nms c str) =>
Enc @[Symbol] ('[] @Symbol) c str -> f (Enc @[Symbol] nms c str)
encodeFAll (Enc @[Symbol] ('[] @Symbol) () ByteString
 -> Either
      EncodeEx
      (Enc
         @[Symbol]
         ((':) @Symbol "enc-B64" ((':) @Symbol "r-ASCII" ('[] @Symbol)))
         ()
         ByteString))
-> (ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString)
-> ByteString
-> Either
     EncodeEx
     (Enc
        @[Symbol]
        ((':) @Symbol "enc-B64" ((':) @Symbol "r-ASCII" ('[] @Symbol)))
        ()
        ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> ByteString -> Enc @[Symbol] ('[] @Symbol) () ByteString
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (ByteString
 -> Either
      EncodeEx
      (Enc
         @[Symbol]
         ((':) @Symbol "enc-B64" ((':) @Symbol "r-ASCII" ('[] @Symbol)))
         ()
         ByteString))
-> ByteString
-> Either
     EncodeEx
     (Enc
        @[Symbol]
        ((':) @Symbol "enc-B64" ((':) @Symbol "r-ASCII" ('[] @Symbol)))
        ()
        ByteString)
forall a b. (a -> b) -> a -> b
$ ByteString
"Hello World"

-- |
-- >>> decodePart @'["enc-B64"] <$> helloAsciiB64
-- Right (UnsafeMkEnc Proxy () "Hello World")
helloAsciiB64PartDec :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString)
helloAsciiB64PartDec :: Either
  EncodeEx
  (Enc
     @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () ByteString)
helloAsciiB64PartDec = forall (xs :: [Symbol]) (xsf :: [Symbol]) c str.
DecodeAll Identity xs xs c str =>
Enc @[Symbol] (Append @Symbol xs xsf) c str
-> Enc @[Symbol] xsf c str
forall (xsf :: [Symbol]) c str.
DecodeAll
  Identity
  ((':) @Symbol "enc-B64" ('[] @Symbol))
  ((':) @Symbol "enc-B64" ('[] @Symbol))
  c
  str =>
Enc
  @[Symbol]
  (Append @Symbol ((':) @Symbol "enc-B64" ('[] @Symbol)) xsf)
  c
  str
-> Enc @[Symbol] xsf c str
decodePart @'["enc-B64"] (Enc
   @[Symbol]
   ((':) @Symbol "enc-B64" ((':) @Symbol "r-ASCII" ('[] @Symbol)))
   ()
   ByteString
 -> Enc
      @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () ByteString)
-> Either
     EncodeEx
     (Enc
        @[Symbol]
        ((':) @Symbol "enc-B64" ((':) @Symbol "r-ASCII" ('[] @Symbol)))
        ()
        ByteString)
-> Either
     EncodeEx
     (Enc
        @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either
  EncodeEx
  (Enc
     @[Symbol]
     ((':) @Symbol "enc-B64" ((':) @Symbol "r-ASCII" ('[] @Symbol)))
     ()
     ByteString)
helloAsciiB64