1 module Databrary.Store.CSV
    2   ( buildCSV
    3   ) where
    4 
    5 import qualified Data.ByteString as BS
    6 import qualified Data.ByteString.Builder as B
    7 import qualified Data.ByteString.Builder.Prim as BP
    8 import qualified Data.ByteString.Char8 as BSC
    9 import Data.ByteString.Internal (c2w)
   10 import Data.Monoid ((<>))
   11 
   12 inter :: B.Builder -> [B.Builder] -> B.Builder
   13 inter _ [] = mempty
   14 inter d (x:l) = x <> mconcat (map (d <>) l)
   15 
   16 csvCell :: BS.ByteString -> B.Builder
   17 csvCell t
   18   | BSC.any (`elem` "\",\r\n") t = q <> BP.primMapByteStringBounded quote t <> q
   19   | otherwise = B.byteString t
   20   where
   21   qw = c2w '"'
   22   q = B.word8 qw
   23   quote = BP.condB (== qw)
   24     (BP.liftFixedToBounded $ const (qw, qw) BP.>$< BP.word8 BP.>*< BP.word8)
   25     (BP.liftFixedToBounded BP.word8)
   26 
   27 csvRow :: [BS.ByteString] -> B.Builder
   28 csvRow r = inter (B.char8 ',') (map csvCell r) <> B.char8 '\n'
   29 
   30 buildCSV :: [[BS.ByteString]] -> B.Builder
   31 buildCSV = mconcat . map csvRow