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