gauge-0.2.0: Gauge/CSV.hs
-- |
-- Module : Gauge.CSV
-- Copyright : (c) 2017 Vincent Hanquez
--
-- a very simple CSV printer
--
-- import qualified for best result
--
module Gauge.CSV
( Row(..)
, outputRow
, Field
, float
, integral
, string
, write
) where
import Data.List (intercalate)
-- | a CSV Field (numerical or string)
--
-- The content inside is properly escaped
newtype Field = Field { unField :: String }
deriving (Show,Eq)
-- | A Row of fields
newtype Row = Row [Field]
deriving (Show,Eq)
-- | Create a field from Double
float :: Double -> Field
float d = Field $ show d
-- | Create a field for numerical integral
integral :: Integral a => a -> Field
integral i = Field $ show (toInteger i)
-- | Create a field from String
string :: String -> Field
string s =
-- potentially a random string need to be escape,
-- first find out how it need to escaped, then
-- escape the data properly.
case needEscape NoEscape s of
NoEscape -> Field s
Escape -> Field ('"' : (s ++ "\""))
EscapeDoubling -> Field ('"' : doubleQuotes s)
where
needEscape EscapeDoubling _ = EscapeDoubling
needEscape e [] = e
needEscape e (x:xs)
| x == '"' = EscapeDoubling
| x `elem` toEscape = needEscape (max e Escape) xs
| otherwise = needEscape e xs
toEscape = ",\r\n"
doubleQuotes [] = ['"']
doubleQuotes (x:xs)
| x == '"' = '"':'"':doubleQuotes xs
| otherwise = x : doubleQuotes xs
-- | Output a row to a String
outputRow :: Row -> String
outputRow (Row fields) = intercalate "," $ map unField fields
-- | 3 Possible modes of escaping:
-- * none
-- * normal quotes escapes
-- * content need doubling because of double quote in content
data Escaping = NoEscape | Escape | EscapeDoubling
deriving (Show,Eq,Ord)
write :: Maybe FilePath
-> Row
-> IO ()
write Nothing _ = return ()
write (Just fp) r = appendFile fp (outputRow r ++ "\r\n")