-- | -- -- Types and functions for handling valid unescaped characters in JSON. -- module Waargonaut.Types.JChar.Unescaped ( -- * Types Unescaped (..) , AsUnescaped (..) -- * Parser , parseUnescaped ) where import Prelude (Eq, Ord (..), Show, (&&), (==), (||)) import Control.Category (id) import Control.Lens (Prism', has, prism') import Data.Foldable (any) import Data.Function (($)) import Data.Functor ((<$>)) import Data.Char (Char, ord) import Data.Maybe (Maybe (..)) import Text.Parser.Char (CharParsing, satisfy) -- $setup -- >>> :set -XOverloadedStrings -- >>> import Control.Monad (return) -- >>> import Data.Either(Either (..), isLeft) -- >>> import Data.Digit (HeXDigit(..)) -- >>> import qualified Data.Digit as D -- >>> import Waargonaut.Decode.Error (DecodeError) -- >>> import Utils ---- -- | Type to specify that this character is unescaped and may be represented -- using a normal Haskell 'Char'. newtype Unescaped = Unescaped Char deriving (Eq, Ord, Show) -- | Typeclass for things that may used as an unescaped JChar. class AsUnescaped a where _Unescaped :: Prism' a Unescaped instance AsUnescaped Unescaped where _Unescaped = id instance AsUnescaped Char where _Unescaped = prism' (\(Unescaped c) -> c) (\c -> if any ($ c) excluded then Nothing else Just (Unescaped c) ) where excluded = [ (== '\NUL') , (== '"') , (== '\\') , \x -> let c = ord x in (c < 0x20 && c > 0x21) || -- "%x20-21" (c < 0x23 && c > 0x5B) || -- "%x23-5B" (c < 0x5D && c > 0x10FFFF) -- "%x5D-10FFFF" ] -- | Parse an unescaped JSON character. -- -- >>> testparse parseUnescaped "a" -- Right (Unescaped 'a') -- -- >>> testparse parseUnescaped "\8728" -- Right (Unescaped '\8728') -- -- >>> testparsetheneof parseUnescaped "a" -- Right (Unescaped 'a') -- -- >>> testparsethennoteof parseUnescaped "ax" -- Right (Unescaped 'a') parseUnescaped :: CharParsing f => f Unescaped parseUnescaped = Unescaped <$> satisfy (has _Unescaped)