packages feed

scrod-0.2026.2.21: source/library/Scrod/Json/Array.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TemplateHaskellQuotes #-}

module Scrod.Json.Array where

import qualified Data.ByteString.Builder as Builder
import qualified Scrod.Extra.Builder as Builder
import qualified Scrod.Extra.Monoid as Monoid
import qualified Scrod.Extra.Parsec as Parsec
import qualified Scrod.Extra.Semigroup as Semigroup
import qualified Scrod.Spec as Spec
import qualified Text.Parsec as Parsec

newtype Array a = MkArray
  { unwrap :: [a]
  }
  deriving (Eq, Ord, Show)

decode :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m a -> Parsec.ParsecT s u m (Array a)
decode p =
  fmap MkArray
    . Parsec.between (Parsec.char '[' <* Parsec.many Parsec.blank) (Parsec.char ']')
    $ Parsec.sepBy (p <* Parsec.many Parsec.blank) (Parsec.char ',' <* Parsec.many Parsec.blank)

encode :: (a -> Builder.Builder) -> Array a -> Builder.Builder
encode b =
  Semigroup.around (Builder.charUtf8 '[') (Builder.charUtf8 ']')
    . Monoid.sepBy (Builder.charUtf8 ',')
    . fmap b
    . unwrap

spec :: (Applicative m, Monad n) => Spec.Spec m n -> n ()
spec s = do
  Spec.named s 'decode $ do
    let p :: (Parsec.Stream t m Char) => Parsec.ParsecT t u m String
        p = Parsec.many1 Parsec.digit

    Spec.it s "succeeds with an empty array" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[]") . Just $ MkArray []

    Spec.it s "succeeds with an empty array with blank space" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[ ]") . Just $ MkArray []

    Spec.it s "succeeds with a single element" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[1]") . Just $ MkArray ["1"]

    Spec.it s "succeeds with a single element with blank space" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[ 1 ]") . Just $ MkArray ["1"]

    Spec.it s "succeeds with multiple elements" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[1,2]") . Just $ MkArray ["1", "2"]

    Spec.it s "succeeds with multiple elements with blank space" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[ 1 , 2 ]") . Just $ MkArray ["1", "2"]

    Spec.it s "fails with leading comma" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[,1]") Nothing

    Spec.it s "fails with trailing comma" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[1,]") Nothing

    Spec.it s "fails with extra comma" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[1,,2]") Nothing

    Spec.it s "fails with only comma" $ do
      Spec.assertEq s (Parsec.parseString (decode p) "[,]") Nothing

  Spec.named s 'encode $ do
    let b = Builder.integerDec

    Spec.it s "encodes empty array" $ do
      Spec.assertEq s (Builder.toString . encode b $ MkArray []) "[]"

    Spec.it s "encodes single element" $ do
      Spec.assertEq s (Builder.toString . encode b $ MkArray [1]) "[1]"

    Spec.it s "encodes multiple elements" $ do
      Spec.assertEq s (Builder.toString . encode b $ MkArray [1, 2]) "[1,2]"