-- | -- 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")