--- * -*- outline-regexp:"--- \\*"; -*-
--- ** doc
{-|

CSV utilities.

-}

--- ** language
{-# LANGUAGE OverloadedStrings    #-}

--- ** exports
module Hledger.Read.CsvUtils (
  CSV, CsvRecord, CsvValue,
  printCSV,
  printTSV,
  -- * Tests
  tests_CsvUtils,
)
where

--- ** imports
import Prelude hiding (Applicative(..))
import Data.List (intersperse)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB

import Hledger.Utils

--- ** doctest setup
-- $setup
-- >>> :set -XOverloadedStrings

type CSV       = [CsvRecord]
type CsvRecord = [CsvValue]
type CsvValue  = Text

printCSV :: [CsvRecord] -> TL.Text
printCSV :: [CsvRecord] -> Text
printCSV = Builder -> Text
TB.toLazyText (Builder -> Text)
-> ([CsvRecord] -> Builder) -> [CsvRecord] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Builder] -> Builder
unlinesB ([Builder] -> Builder)
-> ([CsvRecord] -> [Builder]) -> [CsvRecord] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CsvRecord -> Builder) -> [CsvRecord] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map CsvRecord -> Builder
printRecord
    where printRecord :: CsvRecord -> Builder
printRecord = (Text -> Builder) -> CsvRecord -> Builder
forall m a. Monoid m => (a -> m) -> [a] -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap Text -> Builder
TB.fromText (CsvRecord -> Builder)
-> (CsvRecord -> CsvRecord) -> CsvRecord -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> CsvRecord -> CsvRecord
forall a. a -> [a] -> [a]
intersperse Text
"," (CsvRecord -> CsvRecord)
-> (CsvRecord -> CsvRecord) -> CsvRecord -> CsvRecord
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Text) -> CsvRecord -> CsvRecord
forall a b. (a -> b) -> [a] -> [b]
map Text -> Text
printField
          printField :: Text -> Text
printField = Text -> Text -> Text -> Text
wrap Text
"\"" Text
"\"" (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
T.replace Text
"\"" Text
"\"\""

printTSV :: [CsvRecord] -> TL.Text
printTSV :: [CsvRecord] -> Text
printTSV = Builder -> Text
TB.toLazyText (Builder -> Text)
-> ([CsvRecord] -> Builder) -> [CsvRecord] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Builder] -> Builder
unlinesB ([Builder] -> Builder)
-> ([CsvRecord] -> [Builder]) -> [CsvRecord] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CsvRecord -> Builder) -> [CsvRecord] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map CsvRecord -> Builder
printRecord
    where printRecord :: CsvRecord -> Builder
printRecord = (Text -> Builder) -> CsvRecord -> Builder
forall m a. Monoid m => (a -> m) -> [a] -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap Text -> Builder
TB.fromText (CsvRecord -> Builder)
-> (CsvRecord -> CsvRecord) -> CsvRecord -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> CsvRecord -> CsvRecord
forall a. a -> [a] -> [a]
intersperse Text
"\t" (CsvRecord -> CsvRecord)
-> (CsvRecord -> CsvRecord) -> CsvRecord -> CsvRecord
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Text) -> CsvRecord -> CsvRecord
forall a b. (a -> b) -> [a] -> [b]
map Text -> Text
printField
          printField :: Text -> Text
printField = (Char -> Char) -> Text -> Text
T.map Char -> Char
replaceWhitespace
          replaceWhitespace :: Char -> Char
replaceWhitespace Char
c | Char
c Char -> [Char] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'\t', Char
'\n', Char
'\r'] = Char
' '
          replaceWhitespace Char
c = Char
c

--- ** tests

tests_CsvUtils :: TestTree
tests_CsvUtils :: TestTree
tests_CsvUtils = [Char] -> [TestTree] -> TestTree
testGroup [Char]
"CsvUtils" [
  ]