{-# LANGUAGE DataKinds #-} --{-# LANGUAGE KindSignatures #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} --{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE PartialTypeSignatures #-} --{-# LANGUAGE TypeApplications #-} -- | 'r-B64' is restricted to values that are valid Base64 encodings of some data. -- For example, @Enc '["r-B64"] () T.Text@ can contain encoded binary image. -- -- "enc-B64" can be converted to "r-B64" using @flattenAs@ defined in -- 'Data.TypedEncoding.Instances.Enc.Base64'. -- However, there is no, and there should be no conversion general conversion from "r-B64" back to "enc-B64": -- @Enc '["r-B64"] () T.Text@ is not B64 encoded text, it is B64 encoded something. -- -- @since 0.5.1.0 module Data.TypedEncoding.Instances.Restriction.Base64 where import Data.TypedEncoding.Instances.Support import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Data.Text.Encoding as TE import qualified Data.Text.Lazy.Encoding as TEL import qualified Data.ByteString.Base64 as B64 import qualified Data.ByteString.Base64.Lazy as BL64 import qualified Data.ByteString.Char8 as B8 import qualified Data.TypedEncoding.Instances.Restriction.ASCII as RAscii -- $setup -- >>> :set -XScopedTypeVariables -XKindSignatures -XMultiParamTypeClasses -XDataKinds -XPolyKinds -XPartialTypeSignatures -XFlexibleInstances -XTypeApplications instance Encode (Either EncodeEx) "r-B64" "r-B64" c B.ByteString where encoding = encRB64B instance Encode (Either EncodeEx) "r-B64" "r-B64" c BL.ByteString where encoding = encRB64BL instance Encode (Either EncodeEx) "r-B64" "r-B64" c T.Text where encoding = encRB64T instance Encode (Either EncodeEx) "r-B64" "r-B64" c TL.Text where encoding = encRB64TL instance Encode (Either EncodeEx) "r-B64" "r-B64" c String where encoding = encRB64S -- using lazy decoding to detect errors seems to be the fastest option that is not super hard to code encRB64B :: Encoding (Either EncodeEx) "r-B64" "r-B64" c B.ByteString encRB64B = _implEncodingEx (implVerifyR (B64.decode)) encRB64BL :: Encoding (Either EncodeEx) "r-B64" "r-B64" c BL.ByteString encRB64BL = _implEncodingEx (implVerifyR (BL64.decode)) -- | Converts text to bytestring using UTF8 decoding and then verify encoding in ByteString -- This is safe without verifying ASCII, any non-ASCII text will still convert to ByteString -- but will fail B64.decode (TODO tests would be nice) encRB64T :: Encoding (Either EncodeEx) "r-B64" "r-B64" c T.Text encRB64T = _implEncodingEx (implVerifyR (B64.decode . TE.encodeUtf8)) encRB64TL :: Encoding (Either EncodeEx) "r-B64" "r-B64" c TL.Text encRB64TL = _implEncodingEx (implVerifyR (BL64.decode . TEL.encodeUtf8)) encRB64S :: Encoding (Either EncodeEx) "r-B64" "r-B64" c String encRB64S = _implEncodingEx (implVerifyR (fmap (B64.decode . B8.pack) . either (Left . show) Right . RAscii.encImpl)) -- -- * Decoding instance (Applicative f) => Decode f "r-B64" "r-B64" c str where decoding = decAnyR instance (RecreateErr f, Applicative f) => Validate f "r-B64" "r-B64" c B.ByteString where validation = validR encRB64B instance (RecreateErr f, Applicative f) => Validate f "r-B64" "r-B64" c BL.ByteString where validation = validR encRB64BL instance (RecreateErr f, Applicative f) => Validate f "r-B64" "r-B64" c T.Text where validation = validR encRB64T instance (RecreateErr f, Applicative f) => Validate f "r-B64" "r-B64" c TL.Text where validation = validR encRB64TL instance (RecreateErr f, Applicative f) => Validate f "r-B64" "r-B64" c String where validation = validR encRB64S