packages feed

Frames-0.7.4.2: test/DataCSV.hs

{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}

module DataCSV where

import Data.Bifunctor (first)
import qualified Data.Foldable as F
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Language.Haskell.TH.Syntax (Lift (..))
import qualified Toml
import Validation

data CsvExample = CsvExample
    { name :: String
    , csv :: String
    , generated :: String
    }
    deriving (Lift, Show)

keyText :: Toml.Key -> T.Text
keyText = F.fold . cleanup . map Toml.unPiece . F.toList . Toml.unKey
  where
    -- Top-level table names are parsed as @"name" :|
    -- ["name"]@. Remove that duplication here.
    cleanup [x, y] | x == y = [x]
    cleanup x = x

-- | Parse a TOML file that is a top-level table whose values are all
-- the same type. The @tomland@ codec API is centered around starting
-- with a key, but a top-level table does not have a key, so we must
-- use the lower level 'Toml.parse' and 'Toml.tomlTables' before
-- repeatedly applying the provided 'Toml.TomlCodec'.
parseFileOf :: forall a. Toml.TomlCodec a -> T.Text -> Either [T.Text] [(T.Text, a)]
parseFileOf codec =
    first (map Toml.prettyTomlDecodeError)
        . validationToEither
        . traverse (uncurry go)
        . Toml.toList
        . Toml.tomlTables
        . either (error . show) id
        . Toml.parse
  where
    go :: Toml.Key -> Toml.TOML -> Validation [Toml.TomlDecodeError] (T.Text, a)
    go k v = (keyText k,) <$> Toml.runTomlCodec codec v

parseExamples :: FilePath -> IO (Either [T.Text] [CsvExample])
parseExamples = fmap (fmap (map mkExample) . parseFileOf exampleCodec) . T.readFile
  where
    exampleCodec = Toml.pair (Toml.string "csv") (Toml.string "generated")
    mkExample (name', (csv', generated')) =
        CsvExample (T.unpack name') csv' generated'

-- | Wraps 'parseExamples' to call 'error' on any parse errors.
examplesFrom :: FilePath -> IO [CsvExample]
examplesFrom = fmap (either (error . show) id) . parseExamples