packages feed

succinct-0.0.0.1: components/succinct-json/src/Data/Succinct/Json/Internal/Slurp.hs

{-# LANGUAGE BangPatterns #-}

module Data.Succinct.Json.Internal.Slurp
  ( JsonState(..)
  , slurpText
  , slurpNumber
  ) where

import Data.Text
import Data.Word
import Data.Word8
import Data.Succinct.Json.Standard.Cursor.Internal.Word8
import Prelude                                               hiding (drop)

import qualified Data.Aeson.Parser.Internal  as AP
import qualified Data.Attoparsec.ByteString  as PBS
import qualified Data.ByteString             as BS
import qualified Data.Text                   as T

data JsonState
  = Escaped
  | InJson
  | InString
  | InNumber
  | InIdent

-- | Slurp a JSON string
--
-- Examples:
--
-- >>> :set -XOverloadedStrings
-- >>> slurpText "\"Hello\""
-- Right "Hello"
-- >>> slurpText "123"
-- Left "34: Failed reading: satisfy"
slurpText :: BS.ByteString -> Either Text Text
slurpText bs = case PBS.parseOnly AP.jstring bs of
  Right t -> Right t
  Left e  -> Left (T.pack e)

-- | Slurp a JSON number
--
-- Examples:
--
-- >>> :set -XOverloadedStrings
-- >>> slurpNumber "123, true"
-- "123"
-- >>> slurpNumber "\"Hello\""
-- "\"Hello\""
slurpNumber :: BS.ByteString -> BS.ByteString
slurpNumber bs = let (!cs, _) = BS.unfoldrN (BS.length bs) genNumber (InJson, bs) in cs
    where genNumber :: (JsonState, BS.ByteString) -> Maybe (Word8, (JsonState, BS.ByteString))
          genNumber (InJson, cs) = case BS.uncons cs of
            Just (!d, !ds) | isLeadingDigit d -> Just (d           , (InNumber , ds))
            Just (!d, !ds)                    -> Just (d           , (InJson   , ds))
            Nothing                           -> Nothing
          genNumber (InNumber, cs) = case BS.uncons cs of
            Just (!d, !ds) | isTrailingDigit d -> Just (d           , (InNumber , ds))
            Just (!d, !ds) | d == _quotedbl    -> Just (_parenleft  , (InString , ds))
            _                                  -> Nothing
          genNumber (_, _) = Nothing