{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- | Strings can be encoded as 'Enc "r-ASCII"@ only if they contain only ASCII characters (first 128 characters of the Unicode character set).
--
-- This is sometimes referred to as ASCII-7 and future versions of @type-encoding@ may change @"r-ASCII"@ symbol annotation to reflect this.
--  
-- prop> B8.all ((< 128) . ord) . getPayload @'["r-ASCII"] @() @B.ByteString
-- 
-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds
-- >>> _runEncodings encodings . toEncoding () $ "Hello World" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)
-- Right (UnsafeMkEnc Proxy () "Hello World")
--
-- >>> _runEncodings encodings . toEncoding () $ "\194\160" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)
-- Left (EncodeEx "r-ASCII" (NonAsciiChar '\194'))
--
-- @since 0.1.0.0
module Data.TypedEncoding.Instances.Restriction.ASCII where

import           Data.TypedEncoding.Instances.Support
import           Data.TypedEncoding.Common.Class.Util.StringConstraints

import           Data.TypedEncoding.Internal.Util (explainBool)
import           Data.Char


-- $setup
-- >>> :set -XDataKinds -XTypeApplications
-- >>> import qualified Data.Text as T
-- >>> import qualified Data.ByteString as B
-- >>> import qualified Data.ByteString.Char8 as B8
-- >>> import Test.QuickCheck
-- >>> import Test.QuickCheck.Instances.ByteString()
-- >>> import Data.TypedEncoding
-- >>> :{
-- instance Arbitrary (Enc '["r-ASCII"] () B.ByteString) where 
--      arbitrary =  fmap (unsafeSetPayload ()) 
--                   . flip suchThat (B8.all isAscii) 
--                        $ arbitrary 
-- :}
--


-----------------
-- Encodings  --
-----------------

newtype NonAsciiChar = NonAsciiChar Char deriving (NonAsciiChar -> NonAsciiChar -> Bool
(NonAsciiChar -> NonAsciiChar -> Bool)
-> (NonAsciiChar -> NonAsciiChar -> Bool) -> Eq NonAsciiChar
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NonAsciiChar -> NonAsciiChar -> Bool
$c/= :: NonAsciiChar -> NonAsciiChar -> Bool
== :: NonAsciiChar -> NonAsciiChar -> Bool
$c== :: NonAsciiChar -> NonAsciiChar -> Bool
Eq, Int -> NonAsciiChar -> ShowS
[NonAsciiChar] -> ShowS
NonAsciiChar -> String
(Int -> NonAsciiChar -> ShowS)
-> (NonAsciiChar -> String)
-> ([NonAsciiChar] -> ShowS)
-> Show NonAsciiChar
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [NonAsciiChar] -> ShowS
$cshowList :: [NonAsciiChar] -> ShowS
show :: NonAsciiChar -> String
$cshow :: NonAsciiChar -> String
showsPrec :: Int -> NonAsciiChar -> ShowS
$cshowsPrec :: Int -> NonAsciiChar -> ShowS
Show)

-- * Encoding 

instance Encode (Either EncodeEx) "r-ASCII" "r-ASCII" c Char where
    encoding :: Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c Char
encoding = Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c Char
forall c. Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c Char
encASCIIChar    

instance Char8Find str => Encode (Either EncodeEx) "r-ASCII" "r-ASCII" c str where
    encoding :: Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c str
encoding = Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c str
forall str c.
Char8Find str =>
Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c str
encASCII

encASCIIChar :: Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c Char 
encASCIIChar :: Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c Char
encASCIIChar = (Char -> Either NonAsciiChar Char)
-> Encoding (Either EncodeEx) "r-ASCII" (AlgNm "r-ASCII") c Char
forall (nm :: Symbol) err c str.
(KnownSymbol nm, Show err) =>
(str -> Either err str)
-> Encoding (Either EncodeEx) nm (AlgNm nm) c str
_implEncodingEx (\Char
c -> (Char -> NonAsciiChar) -> (Char, Bool) -> Either NonAsciiChar Char
forall a err. (a -> err) -> (a, Bool) -> Either err a
explainBool Char -> NonAsciiChar
NonAsciiChar (Char
c, Char -> Bool
isAscii Char
c))    

encASCII :: Char8Find str =>  Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c str
encASCII :: Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c str
encASCII = (str -> Either NonAsciiChar str)
-> Encoding (Either EncodeEx) "r-ASCII" (AlgNm "r-ASCII") c str
forall (nm :: Symbol) err c str.
(KnownSymbol nm, Show err) =>
(str -> Either err str)
-> Encoding (Either EncodeEx) nm (AlgNm nm) c str
_implEncodingEx @"r-ASCII" str -> Either NonAsciiChar str
forall str. Char8Find str => str -> Either NonAsciiChar str
encImpl

encImpl :: Char8Find str => str -> Either NonAsciiChar str
encImpl :: str -> Either NonAsciiChar str
encImpl str
str = case (Char -> Bool) -> str -> Maybe Char
forall str. Char8Find str => (Char -> Bool) -> str -> Maybe Char
find (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isAscii) str
str of 
    Maybe Char
Nothing -> str -> Either NonAsciiChar str
forall a b. b -> Either a b
Right str
str
    Just Char
ch -> NonAsciiChar -> Either NonAsciiChar str
forall a b. a -> Either a b
Left (NonAsciiChar -> Either NonAsciiChar str)
-> NonAsciiChar -> Either NonAsciiChar str
forall a b. (a -> b) -> a -> b
$ Char -> NonAsciiChar
NonAsciiChar Char
ch

-- * Decoding

instance (Applicative f) => Decode f "r-ASCII" "r-ASCII" c str where
    decoding :: Decoding f "r-ASCII" "r-ASCII" c str
decoding = Decoding f "r-ASCII" "r-ASCII" c str
forall (r :: Symbol) (f :: * -> *) c str.
(Restriction r, Applicative f) =>
Decoding f r r c str
decAnyR
    
instance (Char8Find str, RecreateErr f, Applicative f) => Validate f "r-ASCII" "r-ASCII" () str where
    validation :: Validation f "r-ASCII" "r-ASCII" () str
validation = Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" () str
-> Validation f "r-ASCII" "r-ASCII" () str
forall (nm :: Symbol) (f :: * -> *) c str.
(Restriction nm, KnownSymbol nm, RecreateErr @* f,
 Applicative f) =>
Encoding (Either EncodeEx) nm nm c str -> Validation f nm nm c str
validR Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" () str
forall str c.
Char8Find str =>
Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c str
encASCII


-- tst = encodeFAll . toEncoding () $ "Hello World" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)
-- tst2 = encodeFAll . toEncoding () $ "\194\160" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)
-- tst3 = encodeFAll . toEncoding () $ "\194\160" :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString)