packages feed

dhall-csv-1.0.0: src/Dhall/Csv/Util.hs

{-# LANGUAGE OverloadedStrings   #-}

module Dhall.Csv.Util (encodeCsvDefault, decodeCsvDefault) where

import Data.List      (sort)
import Data.Text      (Text)
import Data.Csv       (NamedRecord, Record)

import qualified Data.ByteString.Lazy       as ByteString
import qualified Data.ByteString.Lazy.Char8 as ByteString.Char8
import qualified Data.Csv
import qualified Data.HashMap.Strict        as HashMap
import qualified Data.Text.Encoding
import qualified Data.Vector                as Vector

{-| Utility to convert a list of @NamedRecord@ to Text formatted as a CSV.
-}
encodeCsvDefault :: [NamedRecord] -> Text
encodeCsvDefault csv = Data.Text.Encoding.decodeUtf8 $ ByteString.toStrict $ Data.Csv.encodeByName header csv
  where
    header = case csv of
        [] -> Vector.empty
        (m:_) -> Vector.fromList $ sort $ HashMap.keys m

{-| Utility to decode a CSV into a list of records.
    Must specify whether the CSV to decode has header or not.
-}
decodeCsvDefault :: Bool -> Text -> Either String [NamedRecord]
decodeCsvDefault hasHeader
    | hasHeader = decodeCsvWithHeader
    | otherwise = decodeCsvNoHeader

decodeCsvWithHeader :: Text -> Either String [NamedRecord]
decodeCsvWithHeader txt = do
    (_, vec) <- Data.Csv.decodeByName $ ByteString.fromStrict $ Data.Text.Encoding.encodeUtf8 txt
    return $ Vector.toList vec

decodeCsvNoHeader :: Text -> Either String [NamedRecord]
decodeCsvNoHeader txt = do
    vec <- Data.Csv.decode Data.Csv.NoHeader $ ByteString.fromStrict $ Data.Text.Encoding.encodeUtf8 txt
    return $ map addDefaultHeader $ Vector.toList vec

addDefaultHeader :: Record -> NamedRecord
addDefaultHeader = HashMap.fromList . (zip headerBS) . Vector.toList
  where
    header = map (('_' :) . show) ([1..] :: [Int])
    headerBS = map (ByteString.toStrict . ByteString.Char8.pack) header