{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ConstraintKinds #-}
module Data.TypedEncoding.Instances.Restriction.BoundedAlphaNums where
import GHC.TypeLits
import qualified Data.List as L
import Data.Char
import Data.Proxy
import Data.Either
import Data.TypedEncoding.Common.Util.TypeLits
import Data.TypedEncoding.Common.Class.IsStringR
import Data.TypedEncoding.Instances.Support
type family IsBan (s :: Symbol) :: Bool where
IsBan s = AcceptEq ('Text "Not ban restriction encoding " ':<>: ShowType s ) (CmpSymbol (TakeUntil s ":") "r-ban")
type Ban s = (KnownSymbol s, IsBan s ~ 'True)
type instance IsSupersetOpen "r-ASCII" x "r-ban" xs = 'True
instance (Ban s, Algorithm s "r-ban", IsStringR str) => Encode (Either EncodeEx) s "r-ban" c str where
encoding = encFBan
encFBan :: forall s c str .
(
IsStringR str
, Ban s
, Algorithm s "r-ban"
) =>
Encoding (Either EncodeEx) s "r-ban" c str
encFBan = _implEncodingEx @s (verifyBoundedAlphaNum (Proxy :: Proxy s))
instance (KnownSymbol s, Restriction s, Algorithm s "r-ban", Applicative f) => Decode f s "r-ban" c str where
decoding = decAnyR_
instance (KnownSymbol s , Ban s, Algorithm s "r-ban", IsStringR str, RecreateErr f, Applicative f) => Validate f s "r-ban" c str where
validation = validFromEnc' @"r-ban" encFBan
verifyBoundedAlphaNum :: forall s str . (KnownSymbol s, IsStringR str) => Proxy s -> str -> Either String str
verifyBoundedAlphaNum p str =
case (lefts match, notAscii, pattl == inpl) of
(_, Just ch, _) -> Left $ "Not ASCII char in annotation " ++ show ch
(_, _, False) -> Left $ "Input list has wrong size expecting " ++ show pattl ++ " but length " ++ show input ++ " == " ++ show inpl
(e: _, _, _) -> Left e
_ -> Right str
where
patt = L.drop (L.length ("r-ban:" :: String)) . symbolVal $ p
input = toString str
pattl = L.length patt
inpl = L.length input
match = L.zipWith fn input patt
notAscii = L.find (not . isAscii) patt
fn ci cp = case (isAlphaNum ci, isAlphaNum cp, ci <= cp, ci == cp) of
(True, True, True, _) -> Right ()
(_, _, _, True) -> Right ()
(_, True, _, False) -> Left $ show ci ++ " not boulded by " ++ show cp
(_, False, _, False) -> Left $ show ci ++ " not matching " ++ show cp