packages feed

kdl-hs-0.2.0: test/KDL/ParserSpec.hs

{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}

module KDL.ParserSpec (spec) where

import Data.Text (Text)
import Data.Text qualified as Text
import KDL.Parser
import KDL.Types (
  Entry (..),
  Identifier (..),
  Node (..),
  NodeList (..),
  Value (..),
  ValueData (..),
 )
import Skeletest
import Skeletest.Predicate qualified as P
import System.IO.Temp (withSystemTempDirectory)

spec :: Spec
spec = do
  describe "parse" $ do
    it "parses a KDL document" $ do
      let expected =
            newNodeList
              [ newNode
                  "foo"
                  [ newArg $ Number 1.0
                  , newProp "hello" $ Text "world"
                  ]
                  ( Just
                      [ newNode "bar" [] (Just [])
                      ]
                  )
              ]
      parse "foo hello=world 1.0 { bar; }" `shouldBe` Right expected

    it "returns a textual error on parse failure" $ do
      let msg =
            Text.intercalate "\n" $
              [ "1:10:"
              , "  |"
              , "1 | foo hello= 123"
              , "  |          ^^"
              , "unexpected \"= \""
              , "expecting Node Child, Node Space, or Node Terminator"
              ]
      parse "foo hello= 123" `shouldBe` Left msg

  describe "parseFile" $ do
    it "parses a KDL document from a filepath" $ do
      withSystemTempDirectory "" $ \tmpdir -> do
        let file = tmpdir ++ "/test.kdl"
        writeFile file "foo hello=world 1.0 { bar; }"
        let expected =
              newNodeList
                [ newNode
                    "foo"
                    [ newArg $ Number 1.0
                    , newProp "hello" $ Text "world"
                    ]
                    ( Just
                        [ newNode "bar" [] (Just [])
                        ]
                    )
                ]
        parseFile file `shouldSatisfy` P.returns (P.right (P.eq expected))

{----- Helpers -----}

newNodeList :: [Node] -> NodeList
newNodeList nodes =
  NodeList
    { nodes = nodes
    , format = Nothing
    }

newNode :: Text -> [Entry] -> Maybe [Node] -> Node
newNode name entries children =
  Node
    { ann = Nothing
    , name = newIdentifier name
    , entries = entries
    , children = newNodeList <$> children
    , format = Nothing
    }

newArg :: ValueData -> Entry
newArg = newEntry Nothing

newProp :: Text -> ValueData -> Entry
newProp = newEntry . Just

newEntry :: Maybe Text -> ValueData -> Entry
newEntry mName data_ =
  Entry
    { name = newIdentifier <$> mName
    , value =
        Value
          { ann = Nothing
          , data_ = data_
          , format = Nothing
          }
    , format = Nothing
    }

newIdentifier :: Text -> Identifier
newIdentifier value =
  Identifier
    { value = value
    , format = Nothing
    }