packages feed

unsequential-0.5.0.2: attoparsec-integration-test/Main.hs

module Main where

import Rebase.Prelude hiding (takeWhile)
import Data.Attoparsec.ByteString.Char8
import Unsequential
import Interspersed
import Test.Tasty
import Test.Tasty.Runners
import Test.Tasty.HUnit


main =
  defaultMain $
  testGroup "All tests"
  [
    testCase "Primitive" $
    let
      input =
        "{\"year\" : \"2001\", \"month\": \"1\", \"day\": \"2\"}"
      result =
        parseOnly dateOutOfAnyObject input
      in assertEqual (show result) (Right ("2001", "1", "2")) result
    ,
    testCase "In different order" $
    let
      input =
        "{\"month\": \"1\", \"day\": \"2\", \"year\" : \"2001\"}"
      result =
        parseOnly dateOutOfAnyObject input
      in assertEqual (show result) (Right ("2001", "1", "2")) result
    ,
    testCase "With redundant fields" $
    let
      input =
        "{\"redundant1\": \"4\", \"month\": \"1\", \"redundant2\": \"3\", \"day\": \"2\", \"year\" : \"2001\"}"
      result =
        parseOnly dateOutOfAnyObject input
      in assertEqual (show result) (Right ("2001", "1", "2")) result
    ,
    testCase "With trailing fields" $
    let
      input =
        "{\"month\": \"1\", \"day\": \"2\", \"year\" : \"2001\", \"trailing\": \"3\"}"
      result =
        parseOnly dateOutOfAnyObject input
      in assertEqual (show result) (Right ("2001", "1", "2")) result
  ]


-- * Parsers
-------------------------

dateOutOfAnyObject :: Parser (ByteString, ByteString, ByteString)
dateOutOfAnyObject =
  objectWithUnsequentialRows $
  (,,) <$>
  unsequential (interspersed (objectRow (== "year") stringLit)) <*>
  unsequential (interspersed (objectRow (== "month") stringLit)) <*>
  unsequential (interspersed (objectRow (== "day") stringLit))

objectWithUnsequentialRows :: Unsequential (Interspersed Parser) a -> Parser a
objectWithUnsequentialRows unsequentialRows =
  object rows
  where
    rows =
      runInterspersed (runUnsequential unsequentialRows skip <* skipMany skip) comma
      where
        skip =
          interspersed $
          objectRow (const True) stringLit $> ()

-- |
-- Given a rows parser produces a parser of the object.
object :: Parser a -> Parser a
object rows =
  char '{' *> skipSpace *> rows <* skipSpace <* char '}'

-- |
-- Object row parser, which tests the key with a predicate and parses the value.
objectRow :: (ByteString -> Bool) -> Parser a -> Parser a
objectRow keyPredicate valueParser =
  do
    key <- stringLit
    guard (keyPredicate key)
    skipSpace
    char ':'
    skipSpace
    valueParser 

-- |
-- Note: this parser does not satisfy the JSON standards,
-- but suffices the purposes of the test.
stringLit :: Parser ByteString
stringLit =
  char '"' *> takeWhile (/= '"') <* char '"'

comma :: Parser ()
comma =
  skipSpace *> char ',' *> skipSpace