{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE PartialTypeSignatures #-}
module Data.Tapioca
(
CsvMap(..)
, CsvMapped(..)
, ByCsvMap(..)
, DecodeIndexing(..)
, FieldMapping
, (:|)(..)
, (<->)
, nest
, encode
, decode
, header
, (<:>)
, codec
, toRecord
, toNamedRecord
, C.HasHeader(..)
) where
import GHC.Generics
import Data.Tapioca.Internal.ByCsvMap
import Data.Tapioca.Internal.Types.ColSep
import Data.Tapioca.Internal.Types.CsvMap
import Data.Tapioca.Internal.Common
import Data.Tapioca.Internal.Types.Field
import Data.Tapioca.Internal.Types.ParseWithCsvMap
import Data.Tapioca.Internal.Types.ParseRecord()
import qualified Data.Attoparsec.ByteString.Lazy as AB
import qualified Data.Binary.Builder as BB
import qualified Data.ByteString.Lazy as BL
import qualified Data.Csv as C
import qualified Data.Csv.Builder as CB
import qualified Data.Csv.Parser as CP
import Data.Semigroup ((<>))
import qualified Data.Vector as V
encode :: forall r. CsvMapped r => C.HasHeader -> [r] -> BL.ByteString
encode hasHeader items = BB.toLazyByteString $
hdr <> mconcat (CB.encodeRecord . toRecord <$> items)
where hdr = case hasHeader of
C.HasHeader -> CB.encodeHeader (header @r)
C.NoHeader -> mempty
decode :: forall r t. (CsvMapped r, Generic r) => DecodeIndexing r t -> BL.ByteString -> Either String (V.Vector r)
decode indexing csv = C.runParser $ do
records <- parseCsv indexing csv
let parse = case indexing of
DecodeNamed -> parseWithCsvMap
DecodeOrdered _ -> parseWithCsvMap
traverse parse records
parseCsv :: forall r t. CsvMapped r => DecodeIndexing r t -> BL.ByteString -> C.Parser (V.Vector t)
parseCsv indexing csv = toParser . AB.eitherResult . flip AB.parse csv $ case indexing of
DecodeNamed -> snd <$> CP.csvWithHeader C.defaultDecodeOptions
DecodeOrdered C.HasHeader -> CP.header (toEnum $ fromEnum ',') >> CP.csv C.defaultDecodeOptions
DecodeOrdered C.NoHeader -> CP.csv C.defaultDecodeOptions