packages feed

hq-json-0.1.1: src/HQ/JSON/Parser.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE NoImplicitPrelude #-}

module HQ.JSON.Parser where

import Data.Aeson (Value (..))
import Data.Aeson.Decoding (eitherDecodeStrictText, toEitherValue)
import Data.Aeson.Decoding.Text (textToTokens)
import Data.Aeson.KeyMap qualified as KeyMap
import Data.Scientific (Scientific)
import HQ.JSON.Decoder qualified as Decoder
import HQ.JSON.Event (JSONEvent (..))
import HQ.Parser (Parser, lexeme)
import Relude hiding (Compose, id, many, some)
import Text.Megaparsec (getInput, setInput)

-- | Parse a whole JSON value (CLI @VALUE@ arguments, tests).
-- Single validation via aeson.
parseValue :: Text -> Either String Value
parseValue = eitherDecodeStrictText

-- | Parse a value into ordered events ('Value' forgets member order).
-- Single streaming pass (no megaparsec pre-validation).
parseValueEvents :: Text -> Either Text [JSONEvent]
parseValueEvents input = case decodeValueEvents input of
  Left err -> Left (fromString (show err))
  Right events -> Right events

decodeValueEvents :: Text -> Either Decoder.DecodeError [JSONEvent]
decodeValueEvents input = Decoder.decodeTexts [input]

-- | Parse one JSON literal embedded in the DSL directly with aeson.
jsonValueParser :: Parser Value
jsonValueParser = lexeme $ do
  input <- getInput
  case toEitherValue (textToTokens input) of
    Left err -> fail ("invalid JSON value: " <> err)
    Right (v, rest) -> setInput rest >> pure v

jsonTyped :: Text -> (Value -> Maybe a) -> Parser a
jsonTyped msg project = do
  value <- jsonValueParser
  case project value of
    Just a -> pure a
    Nothing -> fail (toString msg)

jsonNumber :: Parser Scientific
jsonNumber = jsonTyped "expected a JSON number" $ \case
  Number n -> Just n
  _ -> Nothing

jsonText :: Parser Text
jsonText = jsonTyped "expected a JSON string" $ \case
  String s -> Just s
  _ -> Nothing

jsonArray :: Parser [Value]
jsonArray = jsonTyped "expected a JSON array" $ \case
  Array items -> Just (toList items)
  _ -> Nothing

jsonObject :: Parser (KeyMap.KeyMap Value)
jsonObject = jsonTyped "expected a JSON object" $ \case
  Object members -> Just members
  _ -> Nothing