content: {-# LANGUAGE OverloadedStrings #-}
  
  module Dhall.Csv.Util (encodeCsvDefault, decodeCsvDefault) where
  
  import Data.Csv  (NamedRecord, Record)
  import Data.List (sort)
  import Data.Text (Text)
  
  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
  
  